From 2eaf48d2662ae124e9f76f33dc74059c4c9a38e8 Mon Sep 17 00:00:00 2001 From: timholy Date: Wed, 10 Sep 2014 09:14:00 -0500 Subject: [PATCH 1/3] Add inline macro via :meta expressions --- base/cartesian.jl | 6 ++++++ base/exports.jl | 3 ++- base/expr.jl | 41 +++++++++++++++++++++++++++++++++++++++++ base/inference.jl | 7 +++---- src/alloc.c | 2 +- src/interpreter.c | 3 +++ src/jltypes.c | 1 + src/julia.h | 2 +- 8 files changed, 58 insertions(+), 7 deletions(-) diff --git a/base/cartesian.jl b/base/cartesian.jl index 9fd9abd0ef382..4c4f6d9bc9b12 100644 --- a/base/cartesian.jl +++ b/base/cartesian.jl @@ -30,6 +30,9 @@ const CARTESIAN_DIMS = 4 # myfunction(A::AbstractArray, I::Int...N) # where N can be an integer or symbol. Currently T...N generates a parser error. macro ngenerate(itersym, returntypeexpr, funcexpr) + if isa(funcexpr, Expr) && funcexpr.head == :macrocall && funcexpr.args[1] == symbol("@inline") + funcexpr = Base._inline(funcexpr.args[2]) + end isfuncexpr(funcexpr) || error("Requires a function expression") esc(ngenerate(itersym, returntypeexpr, funcexpr.args[1], N->sreplace!(copy(funcexpr.args[2]), itersym, N))) end @@ -57,6 +60,9 @@ macro nsplat(itersym, args...) else error("Wrong number of arguments") end + if isa(funcexpr, Expr) && funcexpr.head == :macrocall && funcexpr.args[1] == symbol("@inline") + funcexpr = Base._inline(funcexpr.args[2]) + end isfuncexpr(funcexpr) || error("Second argument must be a function expression") prototype = funcexpr.args[1] body = funcexpr.args[2] diff --git a/base/exports.jl b/base/exports.jl index d6bfb42a36596..d46ceecaba98c 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1384,4 +1384,5 @@ export @inbounds, @simd, @label, - @goto + @goto, + @inline diff --git a/base/expr.jl b/base/expr.jl index f5c297ab9f468..66db044c240aa 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -63,6 +63,13 @@ macro eval(x) :($(esc(:eval))($(Expr(:quote,x)))) end +macro inline(ex) + esc(_inline(ex)) +end + +_inline(ex::Expr) = pushmeta!(ex, :inline) +_inline(arg) = arg + ## some macro utilities ## find_vars(e) = find_vars(e, {}) @@ -95,3 +102,37 @@ function localize_vars(expr, esca) end Expr(:localize, :(()->($expr)), v...) end + +function pushmeta!(ex::Expr, sym::Symbol) + if ex.head == :function + body::Expr = ex.args[2] + if !isempty(body.args) && isa(body.args[1], Expr) && (body.args[1]::Expr).head == :meta + push!((body.args[1]::Expr).args, sym) + else + unshift!(body.args, Expr(:meta, sym)) + end + elseif (ex.head == :(=) && typeof(ex.args[1]) == Expr && ex.args[1].head == :call) + ex = Expr(:function, ex.args[1], Expr(:block, Expr(:meta, sym), ex.args[2])) +# else +# ex = Expr(:withmeta, ex, sym) + end + ex +end + +function popmeta!(body::Expr, sym::Symbol) + if isa(body.args[1],Expr) && (body.args[1]::Expr).head === :meta + metaargs = (body.args[1]::Expr).args + for i = 1:length(metaargs) + if metaargs[i] == sym + if length(metaargs) == 1 + shift!(body.args) # get rid of :meta Expr + else + deleteat!(metaargs, i) # delete this portion of the metadata + end + return true + end + end + end + false +end +popmeta!(arg, sym) = false diff --git a/base/inference.jl b/base/inference.jl index b589538e8c976..834d9d0d94799 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -2496,10 +2496,9 @@ const inline_incompletematch_allowed = false inline_worthy(body, cost::Real) = true function inline_worthy(body::Expr, cost::Real=1.0) # precondition: 0head == simdloop_sym) { return (jl_value_t*)jl_nothing; } + else if (ex->head == meta_sym) { + return (jl_value_t*)jl_nothing; + } jl_errorf("unsupported or misplaced expression %s", ex->head->name); return (jl_value_t*)jl_nothing; } diff --git a/src/jltypes.c b/src/jltypes.c index 26d6226d811aa..70875ea48a72d 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3261,6 +3261,7 @@ void jl_init_types(void) newvar_sym = jl_symbol("newvar"); copyast_sym = jl_symbol("copyast"); simdloop_sym = jl_symbol("simdloop"); + meta_sym = jl_symbol("meta"); } #ifdef __cplusplus diff --git a/src/julia.h b/src/julia.h index b95c9e91d2c1a..13a04464728a5 100644 --- a/src/julia.h +++ b/src/julia.h @@ -421,7 +421,7 @@ extern jl_sym_t *abstracttype_sym; extern jl_sym_t *bitstype_sym; extern jl_sym_t *compositetype_sym; extern jl_sym_t *type_goto_sym; extern jl_sym_t *global_sym; extern jl_sym_t *tuple_sym; extern jl_sym_t *boundscheck_sym; extern jl_sym_t *copyast_sym; -extern jl_sym_t *simdloop_sym; +extern jl_sym_t *simdloop_sym; extern jl_sym_t *meta_sym; // object accessors ----------------------------------------------------------- From 67b58f259962e28b6c59d1fea816f9af8a19f289 Mon Sep 17 00:00:00 2001 From: timholy Date: Thu, 11 Sep 2014 04:29:18 -0500 Subject: [PATCH 2/3] Add missing check for meta_sym in codegen.cpp --- src/codegen.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 01712675e995c..a5d15be94308a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3006,6 +3006,9 @@ static Value *emit_expr(jl_value_t *expr, jl_codectx_t *ctx, bool isboxed, "Warning: could not attach metadata for @simd loop.\n"); return NULL; } + else if (head == meta_sym) { + return literal_pointer_val((jl_value_t*)jl_nothing); // will change as new metadata gets added + } else { if (!strcmp(head->name, "$")) jl_error("syntax: prefix $ in non-quoted expression"); From 106e4f71847df094b1463fd0d86ad827083942d4 Mon Sep 17 00:00:00 2001 From: timholy Date: Thu, 11 Sep 2014 12:56:38 -0500 Subject: [PATCH 3/3] Add tests --- test/Makefile | 2 +- test/meta.jl | 27 +++++++++++++++++++++++++++ test/runtests.jl | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 test/meta.jl diff --git a/test/Makefile b/test/Makefile index f29d46ee09829..50b06f512c40e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -8,7 +8,7 @@ TESTS = all core keywordargs numbers strings unicode collections hashing \ suitesparse complex version pollfd mpfr broadcast socket floatapprox \ priorityqueue readdlm reflection regex float16 combinatorics dates \ sysinfo rounding ranges mod2pi euler show lineedit replcompletions \ - backtrace repl test examples goto llvmcall grisu + backtrace repl test examples goto llvmcall grisu meta default: all diff --git a/test/meta.jl b/test/meta.jl new file mode 100644 index 0000000000000..0958567bea071 --- /dev/null +++ b/test/meta.jl @@ -0,0 +1,27 @@ +# test meta-expressions that annotate blocks of code + +module MetaTest + +using Base.Test + +function f(x) + y = x+5 + z = y*y + q = z/y + m = q-3 +end + +@inline function f_inlined(x) + y = x+5 + z = y*y + q = z/y + m = q-3 +end + +g(x) = f(2x) +g_inlined(x) = f_inlined(2x) + +@test g(3) == g_inlined(3) +@test f(3) == f_inlined(3) + +end diff --git a/test/runtests.jl b/test/runtests.jl index b4a901af28861..379f89e99a78e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,7 +9,7 @@ testnames = [ "floatapprox", "readdlm", "reflection", "regex", "float16", "combinatorics", "sysinfo", "rounding", "ranges", "mod2pi", "euler", "show", "lineedit", "replcompletions", "repl", "test", "examples", "goto", - "llvmcall", "grisu", "nullable" + "llvmcall", "grisu", "nullable", "meta" ] @unix_only push!(testnames, "unicode")