From 73c4646421521dec1e53a09c5cb4bd1c4a2eabe8 Mon Sep 17 00:00:00 2001 From: Shenghang Tsai Date: Tue, 24 Dec 2024 07:29:43 +0800 Subject: [PATCH] Fix for Elixir 1.18 (#58) --- .github/workflows/elixir.yml | 7 +- lib/charms/defm/expander.ex | 186 ++++++++++++++++++----------------- lib/charms/intrinsic.ex | 16 +-- lib/charms/kernel.ex | 8 +- lib/charms/pointer.ex | 46 ++++----- mix.lock | 6 +- test/defm_test.exs | 4 +- 7 files changed, 134 insertions(+), 139 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 914dc89..44a0eca 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -18,13 +18,16 @@ jobs: build: name: Build and test runs-on: ubuntu-latest - + strategy: + fail-fast: false + matrix: + elixir: ["1.17", "1.18"] steps: - uses: actions/checkout@v4 - name: Set up Elixir uses: erlef/setup-beam@v1 with: - elixir-version: "1.17" # [Required] Define the Elixir version + elixir-version: ${{matrix.elixir}} otp-version: "26.0" # [Required] Define the Erlang/OTP version - name: Restore dependencies cache uses: actions/cache@v3 diff --git a/lib/charms/defm/expander.ex b/lib/charms/defm/expander.ex index 45093a5..7074c06 100644 --- a/lib/charms/defm/expander.ex +++ b/lib/charms/defm/expander.ex @@ -228,7 +228,10 @@ defmodule Charms.Defm.Expander do defp expand_call_of_types(call, types, state, env) do {mod, name, args, state, env} = decompose_and_expand_call(call, state, env) + create_call_of_types(mod, name, args, types, state, env) + end + defp create_call_of_types(mod, name, args, types, state, env) do arity = length(args) # By elixir's evaluation order, we should expand the arguments first even the function is not found. @@ -283,11 +286,11 @@ defmodule Charms.Defm.Expander do {{tail_ptr, head_ptr}, _state, _env} = quote do tail_ptr = Charms.Pointer.allocate(Term.t()) - Pointer.store(unquote(l), tail_ptr) + Pointer.store(l, tail_ptr) head_ptr = Charms.Pointer.allocate(Term.t()) {tail_ptr, head_ptr} end - |> expand(state, env) + |> expand_with_bindings(state, env, l: l) # we compile the Enum.reduce/3 to a scf.while in MLIR SCF.while [init] do @@ -302,13 +305,17 @@ defmodule Charms.Defm.Expander do {condition, _state, _env} = quote do enif_get_list_cell( - unquote(env_ptr), - Pointer.load(Term.t(), unquote(tail_ptr)), - unquote(head_ptr), - unquote(tail_ptr) + env_ptr, + Pointer.load(Term.t(), tail_ptr), + head_ptr, + tail_ptr ) > 0 end - |> expand(state, env) + |> expand_with_bindings(state, env, + tail_ptr: tail_ptr, + head_ptr: head_ptr, + env_ptr: env_ptr + ) SCF.condition(condition, acc) >>> [] end @@ -324,11 +331,10 @@ defmodule Charms.Defm.Expander do state = put_mlir_var(state, arg_acc, acc) {head_val, state, env} = - quote(do: Charms.Pointer.load(Charms.Term.t(), unquote(head_ptr))) - |> expand(state, env) + quote(do: Charms.Pointer.load(Charms.Term.t(), head_ptr)) + |> expand_with_bindings(state, env, head_ptr: head_ptr) state = put_mlir_var(state, arg_element, head_val) - # expand the body {body, _state, _env} = expand(body, state, env) SCF.yield(body) >>> [] @@ -351,10 +357,6 @@ defmodule Charms.Defm.Expander do {len, state, env} end - defp expand_std(_module, _fun, _args, _state, _env) do - :not_implemented - end - defp expand_body(body, args, arg_types, state, env) do mlir ctx: state.mlir.ctx do b = @@ -409,7 +411,7 @@ defmodule Charms.Defm.Expander do end invalid_arg -> - raise_compile_error(env, "Invalid operand: #{Macro.to_string(invalid_arg)}") + raise_compile_error(env, "Invalid operand: #{inspect(invalid_arg)}") end end @@ -442,16 +444,73 @@ defmodule Charms.Defm.Expander do end end - defp expand_get_attribute(args, state, env) do + # expand with bindings that are not leaked + defp expand_with_bindings(ast, state, env, bindings) do + state_with_bindings = + for {var, val} <- bindings, reduce: state do + state -> put_mlir_var(state, var, val) + end + + {v, _state, _env} = expand(ast, state_with_bindings, env) + {v, state, env} + end + + defp expand_intrinsics(loc, module, intrinsic_impl, args, state, env) do + try do + {args, state, env} = expand(args, state, env) + state = update_in(state.mlir.required_intrinsic_modules, &MapSet.put(&1, module)) + + v = + apply(module, intrinsic_impl, [ + args, + %Charms.Intrinsic.Opts{ + ctx: state.mlir.ctx, + blk: state.mlir.blk, + loc: loc + } + ]) + + case v do + %m{} when m in [MLIR.Value, MLIR.Type, MLIR.Operation] -> + {v, state, env} + + {ast = {_, _, _}, bindings} when is_list(bindings) -> + :ok = Macro.validate(ast) + expand_with_bindings(ast, state, env, bindings) + + other -> + raise_compile_error( + env, + "Unexpected return type from intrinsic #{module}.#{intrinsic_impl}: #{inspect(other)}" + ) + end + rescue + e -> + raise_compile_error( + env, + "Failed to expand intrinsic #{Exception.message(e)}\n#{Exception.format_stacktrace(__STACKTRACE__)}" + ) + end + end + + defp expand_magic_macros(_loc, {module, fun, _arity} = {String, :length, 1}, args, state, env) do {args, state, env} = expand(args, state, env) + expand_std(module, fun, args, state, env) + end + + defp expand_magic_macros(_loc, {module, fun, _arity} = {Enum, :reduce, 3}, args, state, env) do + expand_std(module, fun, args, state, env) + end + + defp expand_magic_macros(_loc, {Module, :__get_attribute__, 4}, args, state, env) do + {args, _state, _env} = expand(args, state, env) attr = apply(Module, :__get_attribute__, args) |> :erlang.term_to_binary() + {attr, _state, _env} = expand(attr, state, env) env_ptr = beam_env_from_defm!(env, state) - # there is no nested do-block to expand, so it is safe to use regular variable names, as long as the updated state and env are not leaked quote do alias Charms.Pointer alias Charms.Term - attr = unquote(attr) term_ptr = Pointer.allocate(Term.t()) size = String.length(attr) size = value index.casts(size) :: i64() @@ -459,81 +518,33 @@ defmodule Charms.Defm.Expander do buffer = ptr_to_memref(buffer_ptr, size) memref.copy(attr, buffer) zero = const 0 :: i32() - enif_binary_to_term(unquote(env_ptr), buffer_ptr, size, term_ptr, zero) + enif_binary_to_term(env_ptr, buffer_ptr, size, term_ptr, zero) Pointer.load(Term.t(), term_ptr) end - |> expand(state, env) - end - - defp expand_intrinsics(loc, module, intrinsic_impl, args, state, env) do - {args, state, env} = expand(args, state, env) - state = update_in(state.mlir.required_intrinsic_modules, &MapSet.put(&1, module)) - - v = - apply(module, intrinsic_impl, [ - args, - %Charms.Intrinsic.Opts{ - ctx: state.mlir.ctx, - blk: state.mlir.blk, - loc: loc - } - ]) - - case v do - %m{} when m in [MLIR.Value, MLIR.Type, MLIR.Operation] -> - {v, state, env} - - ast = {_, _, _} -> - # do not leak variables created in the macro - {v, _state, _env} = expand(ast, state, env) - {v, state, env} - - other -> - raise_compile_error( - env, - "Unexpected return type from intrinsic #{module}.#{intrinsic_impl}: #{inspect(other)}" - ) - end + |> expand_with_bindings(state, env, attr: attr, env_ptr: env_ptr) end - defp expand_magic_macros(loc, {module, fun, arity} = mfa, args, state, env) do - cond do - export_intrinsics?(module, fun, arity) -> - intrinsic_impl = module.__intrinsics__(fun, arity) - expand_intrinsics(loc, module, intrinsic_impl, args, state, env) - - mfa == {Module, :__get_attribute__, 4} -> - expand_get_attribute(args, state, env) - - (res = expand_std(module, fun, args, state, env)) != :not_implemented -> - res - - true -> - quote(do: unquote(module).unquote(fun)(unquote_splicing(args))) - |> expand_call_of_types([], state, env) + defp expand_magic_macros(loc, {module, fun, arity}, args, state, env) do + if export_intrinsics?(module, fun, arity) do + intrinsic_impl = module.__intrinsics__(fun, arity) + expand_intrinsics(loc, module, intrinsic_impl, args, state, env) + else + create_call_of_types(module, fun, args, [], state, env) end end defp expand_remote_macro(meta, {module, fun, arity} = mfa, args, state, env) do loc = MLIR.Location.from_env(env) - try do - case Macro.Env.expand_require(env, meta, module, fun, arity, - trace: true, - check_deprecations: false - ) do - {:macro, module, callback} -> - expand_macro(meta, module, fun, args, callback, state, env) - - :error -> - expand_magic_macros(loc, mfa, args, state, env) - end - rescue - e -> - raise_compile_error( - env, - "Failed to expand macro #{module}.#{fun}/#{arity}: #{Exception.message(e)}" - ) + case Macro.Env.expand_require(env, meta, module, fun, arity, + trace: true, + check_deprecations: false + ) do + {:macro, module, callback} -> + expand_macro(meta, module, fun, args, callback, state, env) + + :error -> + expand_magic_macros(loc, mfa, args, state, env) end end @@ -1270,15 +1281,12 @@ defmodule Charms.Defm.Expander do {args, state, env} = expand_list(args, state, env) try do - quote do - unquote(env.module).unquote(fun)(unquote_splicing(args)) - end - |> expand_call_of_types([], state, env) + create_call_of_types(env.module, fun, args, [], state, env) rescue e -> raise_compile_error( env, - "Failed to expand local function #{fun}/#{length(args)}: #{Exception.message(e)}" + "Failed to expand local function #{fun}/#{length(args)}: #{Exception.message(e)}, #{Exception.format_stacktrace(__STACKTRACE__)}" ) end end @@ -1340,7 +1348,7 @@ defmodule Charms.Defm.Expander do put_mlir_var(state, name, val) end - defp get_mlir_var(state, {name, _meta, _ctx}) do + defp get_mlir_var(state, {name, _meta, ctx}) when is_atom(ctx) do Map.get(state.mlir.vars, name) end diff --git a/lib/charms/intrinsic.ex b/lib/charms/intrinsic.ex index faf3659..a9b2637 100644 --- a/lib/charms/intrinsic.ex +++ b/lib/charms/intrinsic.ex @@ -43,20 +43,8 @@ defmodule Charms.Intrinsic do %Charms.Intrinsic.Opts{} = var!(charms_intrinsic_internal_opts) end - case opts do - {:when, when_meta, [opts | clauses]} -> - {:when, when_meta, - [ - quote do - unquote(intrinsic_name_ast)(unquote(args), unquote(opts)) - end - | clauses - ]} - - _ -> - quote do - unquote(intrinsic_name_ast)(unquote(args), unquote(opts)) - end + quote do + unquote(intrinsic_name_ast)(unquote(args), unquote(opts)) end end diff --git a/lib/charms/kernel.ex b/lib/charms/kernel.ex index 3253817..cb6b598 100644 --- a/lib/charms/kernel.ex +++ b/lib/charms/kernel.ex @@ -84,10 +84,10 @@ defmodule Charms.Kernel do raise ArgumentError, "Not an integer type to negate, unsupported type: #{to_string(t)}" end - quote bind_quoted: [v: value, t: t] do - one = const 1 :: t - value arith.xori(v, one) :: t - end + {quote do + one = const 1 :: t + value arith.xori(value, one) :: t + end, t: t, value: value} end @doc false diff --git a/lib/charms/pointer.ex b/lib/charms/pointer.ex index 0de5a64..a51e70b 100644 --- a/lib/charms/pointer.ex +++ b/lib/charms/pointer.ex @@ -8,15 +8,15 @@ defmodule Charms.Pointer do use Charms.Intrinsic alias Charms.Intrinsic.Opts alias Beaver.MLIR.{Type} - alias Beaver.MLIR.Dialect.{MemRef, Index, Arith} + alias Beaver.MLIR.Dialect.{MemRef, Index, Arith, LLVM} @doc """ Allocates a single element of the given `elem_type`, returning a pointer to it. """ defintrinsic allocate(elem_type) do - quote bind_quoted: [elem_type: elem_type] do - Charms.Pointer.allocate(elem_type, 1) - end + {quote do + Charms.Pointer.allocate(elem_type, 1) + end, elem_type: elem_type} end @doc """ @@ -52,6 +52,15 @@ defmodule Charms.Pointer do end end + @doc false + def memref_ptr?(%MLIR.Type{} = t) do + MLIR.CAPI.mlirTypeIsAMemRef(t) |> Beaver.Native.to_term() + end + + def memref_ptr?(%MLIR.Value{} = ptr) do + MLIR.Value.type(ptr) |> memref_ptr?() + end + @doc """ Loads a value of `type` from the given pointer `ptr`. """ @@ -59,8 +68,8 @@ defmodule Charms.Pointer do %Opts{ctx: ctx, blk: blk, loc: loc} = __IR__ if MLIR.equal?(MLIR.Value.type(ptr), ~t{!llvm.ptr}) do - quote bind_quoted: [type: type, ptr: ptr] do - value llvm.load(ptr) :: type + mlir ctx: ctx, blk: blk do + LLVM.load(ptr, loc: loc) >>> type end else mlir ctx: ctx, blk: blk do @@ -70,22 +79,13 @@ defmodule Charms.Pointer do end end - @doc false - def memref_ptr?(%MLIR.Type{} = t) do - MLIR.CAPI.mlirTypeIsAMemRef(t) |> Beaver.Native.to_term() - end - - def memref_ptr?(%MLIR.Value{} = ptr) do - MLIR.Value.type(ptr) |> memref_ptr?() - end - defintrinsic load(%MLIR.Value{} = ptr) do t = MLIR.Value.type(ptr) if memref_ptr?(t) do - quote do - Charms.Pointer.load(unquote(MLIR.CAPI.mlirShapedTypeGetElementType(t)), unquote(ptr)) - end + {quote do + Charms.Pointer.load(Charms.Pointer.element_type(ptr), ptr) + end, ptr: ptr} else raise ArgumentError, "Pointer is not typed, use load/2 to specify the pointer type" end @@ -177,13 +177,9 @@ defmodule Charms.Pointer do t = MLIR.Value.type(ptr) if memref_ptr?(t) do - quote do - Charms.Pointer.element_ptr( - unquote(MLIR.CAPI.mlirShapedTypeGetElementType(t)), - unquote(ptr), - unquote(n) - ) - end + {quote do + Charms.Pointer.element_ptr(elem_type, ptr, n) + end, ptr: ptr, n: n, elem_type: MLIR.CAPI.mlirShapedTypeGetElementType(t)} else raise ArgumentError, "Pointer is not typed, use element_ptr/3 to specify the pointer type" end diff --git a/mix.lock b/mix.lock index aeaface..66dd140 100644 --- a/mix.lock +++ b/mix.lock @@ -4,15 +4,15 @@ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "credo": {:hex, :credo, "1.7.10", "6e64fe59be8da5e30a1b96273b247b5cf1cc9e336b5fd66302a64b25749ad44d", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "71fbc9a6b8be21d993deca85bf151df023a3097b01e09a2809d460348561d8cd"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.42", "f23d856f41919f17cd06a493923a722d87a2d684f143a1e663c04a2b93100682", [:mix], [], "hexpm", "6915b6ca369b5f7346636a2f41c6a6d78b5af419d61a611079189233358b8b8b"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"}, "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, - "kinda": {:hex, :kinda, "0.9.3", "936e1e7a6e54943b078366319af4de4cce5f2ff2a9462e03187fcf762278f93c", [:mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "6661b01420625474ebe1cbb592e8ea450d6434943c6c04800183ccee62fbfbd7"}, + "kinda": {:hex, :kinda, "0.9.4", "007e25491bcd3af8a95e0179d9044362dc336920bbe5dbd6515196a5e938b201", [:mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "a5ec71839edf88e52f3e68a2c2210b9a9a00ee414ff369e9801b1184b3844447"}, "llvm_config": {:hex, :llvm_config, "0.1.1", "2c4a1e16c51d18528014c35783bc7779cb8b8b0d0ed2a5f3b4d35709a819e492", [:mix], [], "hexpm", "026376818206f1ff91f48946aa0272cac2bae2d00e81916d1275b3f9ca72a7ea"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, - "makeup_elixir": {:hex, :makeup_elixir, "1.0.0", "74bb8348c9b3a51d5c589bf5aebb0466a84b33274150e3b6ece1da45584afc82", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49159b7d7d999e836bedaf09dcf35ca18b312230cf901b725a64f3f42e407983"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, diff --git a/test/defm_test.exs b/test/defm_test.exs index b37f1c7..9160034 100644 --- a/test/defm_test.exs +++ b/test/defm_test.exs @@ -78,7 +78,7 @@ defmodule DefmTest do line = __ENV__.line assert_raise CompileError, - ~r"Failed to expand macro Elixir.DifferentCalls.something/1.+function something not found in module DifferentCalls", + ~r"function something not found in module DifferentCalls", fn -> defmodule Undefined do use Charms @@ -166,7 +166,7 @@ defmodule DefmTest do end test "enif type mismatch" do - assert_raise ArgumentError, ~r/Expected a value of type i32, got f32/, fn -> + assert_raise CompileError, ~r/Expected a value of type i32, got f32/, fn -> defmodule MismatchEnifType do use Charms alias Charms.Term