Introduce debugging symbols (#424)
* Add fann type and to_fann fun * Add fann() to funcall * Add fann() to closure * Add fann() to set_state * Add fann() to remote_u * Add fann() to remote * Add fann() to proj * Add fann() to set_proj * Add fann() to def and def_u * Add fann() to op * Add fann() to let * Add fann() to lam * Add fann() to builtin_u * Add missing functions specs * Dead code removal * Fix the spec for compute_state_layout * Add fann() to var * Add fann() to switch * Add fann() to lit and get_state * Add fann() to builtin * Add fann() to con * Add fann() to tuple * Add fann() to nil * Fix missing fann() in tuple fexpr() * Add dbgloc instruction to fate * Add instructions lines to the debugging result * Fix compiler tests * Fix calldata tests * Rname Ann to FAnn when the type is fann() * Add line to fann() * Change attributes for DBGLOC instruction * Add file to fann() * Add file to aeso_syntax:ann() * Fix dialyzer warning * Remove fann() from fsplit_pat() and fpat() * Fill out empty fann() when possible * Save debug locations for child contracts * Include DBGLOC instructions in the compiler output * Return an empty string instead of no_file atom * Wrap args of DBGLOC in immediate tuple * Upgrade aebytecode ref in rebar.config * Add DBG_DEF and DBG_UNDEF * Do not DBG_DEF vars with % prefix * Do not use DBG_DEF and DBG_UNDEF on args * Fix dbg_undef for args * Rename DBGLOC to DBG_LOC * Remove column from DBG_LOC * Add missing dbg_loc in to_scode1 * Keep a single DBG_LOC instruction per line * Remove col from fann * Add DBG_LOC op to step at function sig * Remove the variable-register map from debug output * Use get_value/3 to handle default * Use lookup instead of lookup_all * List only needed attributes * Make debug ops impure * Split complicated code and add comment * Fix annotations * Fix indenting * Remove dbg_loc before closure * Add dbg_loc in to_scode * Add DBG_CALL and DBG_RETURN * Separate the split at CALL_T and loop * Revert "Separate the split at CALL_T and loop" This reverts commit 4ea823a7ca798c756b20cee32f928f41092c4959. * Revert "Add DBG_CALL and DBG_RETURN" This reverts commit c406c6feb09b6a5bb859c38d634f08208c901e5a. * Disable tail call optimization for better debug call stack * Rename env.debug to env.debug_info * Upgrade aebytecode: Add DBG_CONTRACT * Add DBG_CONTRACT instruction * Check if a var name is fresh in separate function * Add DBG_CONTRACT and DBG_LOC before DBG_DEF * Save fresh names of pattern variables * Implement fsplit_pat_vars for assign * Set fann for switches * Revert "Save fresh names of pattern variables" This reverts commit d2473f982996336131477df2b2115c04a55a62cb. * Add DBG_DEF for switch pattern vars * Fix the inability to pattern match constructors * Upgrade aebytecode dep * Upgrade aebytecode dep * Update the lock file * Add annotations to fexpr var * Fix issues with pretty-printing of fexprs * Use FAnn instead of get_fann(Body) * Upgrade aebytecode version * Fix pp_fpat * Fix pattern matching on fpat * Update rename when a new rename comes up * Upgrade aebytecode * Remove the getopt dep * Fix calldata tests * Remove file committed by mistake * Remove location anns from contract call type
This commit is contained in:
parent
7bac15949c
commit
c395849684
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- API functions for encoding/decoding Sophia values to/from FATE.
|
- API functions for encoding/decoding Sophia values to/from FATE.
|
||||||
### Changed
|
### Changed
|
||||||
### Removed
|
### Removed
|
||||||
|
- Remove the mapping from variables to FATE registers from the compilation output.
|
||||||
### Fixed
|
### Fixed
|
||||||
- Warning about unused include when there is no include.
|
- Warning about unused include when there is no include.
|
||||||
|
|
||||||
|
@ -53,8 +53,6 @@ The **pp_** options all print to standard output the following:
|
|||||||
|
|
||||||
The option `include_child_contract_symbols` includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain.
|
The option `include_child_contract_symbols` includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain.
|
||||||
|
|
||||||
The option `debug_info` includes information related to debugging in the compiler output. Currently this option only includes the mapping from variables to registers.
|
|
||||||
|
|
||||||
#### Options to control which compiler optimizations should run:
|
#### Options to control which compiler optimizations should run:
|
||||||
|
|
||||||
By default all optimizations are turned on, to disable an optimization, it should be
|
By default all optimizations are turned on, to disable an optimization, it should be
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.2.0"}}}
|
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.3.0"}}}
|
||||||
, {getopt, "1.0.1"}
|
|
||||||
, {eblake2, "1.0.0"}
|
, {eblake2, "1.0.0"}
|
||||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
||||||
]}.
|
]}.
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{"1.2.0",
|
{"1.2.0",
|
||||||
[{<<"aebytecode">>,
|
[{<<"aebytecode">>,
|
||||||
{git,"https://github.com/aeternity/aebytecode.git",
|
{git,"https://github.com/aeternity/aebytecode.git",
|
||||||
{ref,"2a0a397afad6b45da52572170f718194018bf33c"}},
|
{ref,"b38349274fc2bed98d7fe86877e6e1a2df302109"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
{ref,"eb68fe331bd476910394966b7f5ede7a74d37e35"}},
|
{ref,"177bf604b2a05e940f92cf00e96e6e269e708245"}},
|
||||||
1},
|
1},
|
||||||
{<<"base58">>,
|
{<<"base58">>,
|
||||||
{git,"https://github.com/aeternity/erl-base58.git",
|
{git,"https://github.com/aeternity/erl-base58.git",
|
||||||
@ -16,7 +16,7 @@
|
|||||||
{git,"https://github.com/aeternity/enacl.git",
|
{git,"https://github.com/aeternity/enacl.git",
|
||||||
{ref,"793ddb502f7fe081302e1c42227dca70b09f8e17"}},
|
{ref,"793ddb502f7fe081302e1c42227dca70b09f8e17"}},
|
||||||
2},
|
2},
|
||||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
|
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},1},
|
||||||
{<<"jsx">>,
|
{<<"jsx">>,
|
||||||
{git,"https://github.com/talentdeficit/jsx.git",
|
{git,"https://github.com/talentdeficit/jsx.git",
|
||||||
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
|
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
|
||||||
|
@ -351,11 +351,11 @@ bind_contract(Typing, {Contract, Ann, Id, _Impls, Contents}, Env)
|
|||||||
Sys = [{origin, system}],
|
Sys = [{origin, system}],
|
||||||
TypeOrFresh = fun({typed, _, _, Type}) -> Type; (_) -> fresh_uvar(Sys) end,
|
TypeOrFresh = fun({typed, _, _, Type}) -> Type; (_) -> fresh_uvar(Sys) end,
|
||||||
Fields =
|
Fields =
|
||||||
[ {field_t, AnnF, Entrypoint, contract_call_type(Type)}
|
[ {field_t, AnnF, Entrypoint, contract_call_type(aeso_syntax:set_ann(Sys, Type))}
|
||||||
|| {fun_decl, AnnF, Entrypoint, Type = {fun_t, _, _, _, _}} <- Contents ] ++
|
|| {fun_decl, AnnF, Entrypoint, Type = {fun_t, _, _, _, _}} <- Contents ] ++
|
||||||
[ {field_t, AnnF, Entrypoint,
|
[ {field_t, AnnF, Entrypoint,
|
||||||
contract_call_type(
|
contract_call_type(
|
||||||
{fun_t, AnnF, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)})
|
{fun_t, Sys, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)})
|
||||||
}
|
}
|
||||||
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents,
|
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents,
|
||||||
Name =/= "init"
|
Name =/= "init"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -119,7 +119,7 @@ from_string1(ContractString, Options) ->
|
|||||||
, warnings := Warnings } = string_to_code(ContractString, Options),
|
, warnings := Warnings } = string_to_code(ContractString, Options),
|
||||||
#{ child_con_env := ChildContracts } = FCodeEnv,
|
#{ child_con_env := ChildContracts } = FCodeEnv,
|
||||||
SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}),
|
SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}),
|
||||||
{FateCode, VarsRegs} = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
|
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
|
||||||
pp_assembler(FateCode, Options),
|
pp_assembler(FateCode, Options),
|
||||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
{ok, Version} = version(),
|
{ok, Version} = version(),
|
||||||
@ -132,13 +132,7 @@ from_string1(ContractString, Options) ->
|
|||||||
payable => maps:get(payable, FCode),
|
payable => maps:get(payable, FCode),
|
||||||
warnings => Warnings
|
warnings => Warnings
|
||||||
},
|
},
|
||||||
ResDbg = Res#{variables_registers => VarsRegs},
|
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}.
|
||||||
FinalRes =
|
|
||||||
case proplists:get_value(debug_info, Options, false) of
|
|
||||||
true -> ResDbg;
|
|
||||||
false -> Res
|
|
||||||
end,
|
|
||||||
{ok, maybe_generate_aci(FinalRes, FoldedTypedAst, Options)}.
|
|
||||||
|
|
||||||
maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
||||||
case proplists:get_value(aci, Options) of
|
case proplists:get_value(aci, Options) of
|
||||||
@ -192,7 +186,7 @@ check_call(Source, FunName, Args, Options) ->
|
|||||||
check_call1(ContractString0, FunName, Args, Options) ->
|
check_call1(ContractString0, FunName, Args, Options) ->
|
||||||
case add_extra_call(ContractString0, {call, FunName, Args}, Options) of
|
case add_extra_call(ContractString0, {call, FunName, Args}, Options) of
|
||||||
{ok, CallName, Code} ->
|
{ok, CallName, Code} ->
|
||||||
{def, _, FcodeArgs} = get_call_body(CallName, Code),
|
{def, _, _, FcodeArgs} = get_call_body(CallName, Code),
|
||||||
{ok, FunName, [ aeso_fcode_to_fate:term_to_fate(A) || A <- FcodeArgs ]};
|
{ok, FunName, [ aeso_fcode_to_fate:term_to_fate(A) || A <- FcodeArgs ]};
|
||||||
Err = {error, _} ->
|
Err = {error, _} ->
|
||||||
Err
|
Err
|
||||||
@ -204,7 +198,7 @@ add_extra_call(Contract0, Call, Options) ->
|
|||||||
#{fcode := OrgFcode
|
#{fcode := OrgFcode
|
||||||
, fcode_env := #{child_con_env := ChildContracts}
|
, fcode_env := #{child_con_env := ChildContracts}
|
||||||
, ast := Ast} = string_to_code(Contract0, Options),
|
, ast := Ast} = string_to_code(Contract0, Options),
|
||||||
{FateCode, _} = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
|
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
|
||||||
%% collect all hashes and compute the first name without hash collision to
|
%% collect all hashes and compute the first name without hash collision to
|
||||||
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
||||||
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
||||||
|
@ -52,7 +52,8 @@
|
|||||||
tailpos = true,
|
tailpos = true,
|
||||||
child_contracts = #{},
|
child_contracts = #{},
|
||||||
saved_fresh_names = #{},
|
saved_fresh_names = #{},
|
||||||
options = [] }).
|
options = [],
|
||||||
|
debug_info = false }).
|
||||||
|
|
||||||
%% -- Debugging --------------------------------------------------------------
|
%% -- Debugging --------------------------------------------------------------
|
||||||
|
|
||||||
@ -81,24 +82,16 @@ code_error(Err) ->
|
|||||||
compile(FCode, SavedFreshNames, Options) ->
|
compile(FCode, SavedFreshNames, Options) ->
|
||||||
compile(#{}, FCode, SavedFreshNames, Options).
|
compile(#{}, FCode, SavedFreshNames, Options).
|
||||||
compile(ChildContracts, FCode, SavedFreshNames, Options) ->
|
compile(ChildContracts, FCode, SavedFreshNames, Options) ->
|
||||||
try
|
|
||||||
compile1(ChildContracts, FCode, SavedFreshNames, Options)
|
|
||||||
after
|
|
||||||
put(variables_registers, undefined)
|
|
||||||
end.
|
|
||||||
|
|
||||||
compile1(ChildContracts, FCode, SavedFreshNames, Options) ->
|
|
||||||
#{ contract_name := ContractName,
|
#{ contract_name := ContractName,
|
||||||
functions := Functions } = FCode,
|
functions := Functions } = FCode,
|
||||||
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options),
|
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options),
|
||||||
SFuns1 = optimize_scode(SFuns, Options),
|
SFuns1 = optimize_scode(SFuns, Options),
|
||||||
FateCode = to_basic_blocks(SFuns1),
|
FateCode = to_basic_blocks(SFuns1),
|
||||||
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
|
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
|
||||||
FateCode1 = case proplists:get_value(include_child_contract_symbols, Options, false) of
|
case proplists:get_value(include_child_contract_symbols, Options, false) of
|
||||||
false -> FateCode;
|
false -> FateCode;
|
||||||
true -> add_child_symbols(ChildContracts, FateCode)
|
true -> add_child_symbols(ChildContracts, FateCode)
|
||||||
end,
|
end.
|
||||||
{FateCode1, get_variables_registers()}.
|
|
||||||
|
|
||||||
make_function_id(X) ->
|
make_function_id(X) ->
|
||||||
aeb_fate_code:symbol_identifier(make_function_name(X)).
|
aeb_fate_code:symbol_identifier(make_function_name(X)).
|
||||||
@ -123,31 +116,15 @@ functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Opt
|
|||||||
|
|
||||||
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, SavedFreshNames, Options) ->
|
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, SavedFreshNames, Options) ->
|
||||||
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
||||||
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
|
Attrs = [ A || A <- Attrs0, A == private orelse A == payable ],
|
||||||
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, SavedFreshNames, Options),
|
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, SavedFreshNames, Options),
|
||||||
[ add_variables_register(Env, Arg, Register) ||
|
ArgsNames = [ X || {X, _} <- lists:reverse(Env#env.vars) ],
|
||||||
proplists:get_value(debug_info, Options, false),
|
|
||||||
{Arg, Register} <- Env#env.vars ],
|
%% DBG_LOC is added before the function body to make it possible to break
|
||||||
|
%% at the function signature
|
||||||
SCode = to_scode(Env, Body),
|
SCode = to_scode(Env, Body),
|
||||||
{Attrs, {ArgTypes, ResType1}, SCode}.
|
DbgSCode = dbg_contract(Env) ++ dbg_loc(Env, Attrs0) ++ dbg_scoped_vars(Env, ArgsNames, SCode),
|
||||||
|
{Attrs, {ArgTypes, ResType1}, DbgSCode}.
|
||||||
get_variables_registers() ->
|
|
||||||
case get(variables_registers) of
|
|
||||||
undefined -> #{};
|
|
||||||
Vs -> Vs
|
|
||||||
end.
|
|
||||||
|
|
||||||
add_variables_register(Env = #env{saved_fresh_names = SavedFreshNames}, Name, Register) ->
|
|
||||||
Olds = get_variables_registers(),
|
|
||||||
RealName = maps:get(Name, SavedFreshNames, Name),
|
|
||||||
FunName =
|
|
||||||
case Env#env.current_function of
|
|
||||||
event -> "Chain.event";
|
|
||||||
{entrypoint, BinName} -> binary_to_list(BinName);
|
|
||||||
{local_fun, QualName} -> lists:last(QualName)
|
|
||||||
end,
|
|
||||||
New = {Env#env.contract, FunName, RealName},
|
|
||||||
put(variables_registers, Olds#{New => Register}).
|
|
||||||
|
|
||||||
-define(tvars, '$tvars').
|
-define(tvars, '$tvars').
|
||||||
|
|
||||||
@ -201,13 +178,13 @@ init_env(ChildContracts, ContractName, FunNames, Name, Args, SavedFreshNames, Op
|
|||||||
current_function = Name,
|
current_function = Name,
|
||||||
options = Options,
|
options = Options,
|
||||||
tailpos = true,
|
tailpos = true,
|
||||||
saved_fresh_names = SavedFreshNames }.
|
saved_fresh_names = SavedFreshNames,
|
||||||
|
debug_info = proplists:get_value(debug_info, Options, false) }.
|
||||||
|
|
||||||
next_var(#env{ vars = Vars }) ->
|
next_var(#env{ vars = Vars }) ->
|
||||||
1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]).
|
1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]).
|
||||||
|
|
||||||
bind_var(Name, Var, Env = #env{ vars = Vars }) ->
|
bind_var(Name, Var, Env = #env{ vars = Vars }) ->
|
||||||
proplists:get_value(debug_info, Env#env.options, false) andalso add_variables_register(Env, Name, Var),
|
|
||||||
Env#env{ vars = [{Name, Var} | Vars] }.
|
Env#env{ vars = [{Name, Var} | Vars] }.
|
||||||
|
|
||||||
bind_local(Name, Env) ->
|
bind_local(Name, Env) ->
|
||||||
@ -234,7 +211,7 @@ serialize_contract_code(Env, C) ->
|
|||||||
Options = Env#env.options,
|
Options = Env#env.options,
|
||||||
SavedFreshNames = Env#env.saved_fresh_names,
|
SavedFreshNames = Env#env.saved_fresh_names,
|
||||||
FCode = maps:get(C, Env#env.child_contracts),
|
FCode = maps:get(C, Env#env.child_contracts),
|
||||||
{FateCode, _} = compile1(Env#env.child_contracts, FCode, SavedFreshNames, Options),
|
FateCode = compile(Env#env.child_contracts, FCode, SavedFreshNames, Options),
|
||||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
{ok, Version} = aeso_compiler:version(),
|
{ok, Version} = aeso_compiler:version(),
|
||||||
OriginalSourceCode = proplists:get_value(original_src, Options, ""),
|
OriginalSourceCode = proplists:get_value(original_src, Options, ""),
|
||||||
@ -268,44 +245,44 @@ lit_to_fate(Env, L) ->
|
|||||||
term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
|
term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
|
||||||
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E).
|
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E).
|
||||||
|
|
||||||
term_to_fate(GlobEnv, _Env, {lit, L}) ->
|
term_to_fate(GlobEnv, _Env, {lit, _, L}) ->
|
||||||
lit_to_fate(GlobEnv, L);
|
lit_to_fate(GlobEnv, L);
|
||||||
%% negative literals are parsed as 0 - N
|
%% negative literals are parsed as 0 - N
|
||||||
term_to_fate(_GlobEnv, _Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) ->
|
term_to_fate(_GlobEnv, _Env, {op, _, '-', [{lit, _, {int, 0}}, {lit, _, {int, N}}]}) ->
|
||||||
aeb_fate_data:make_integer(-N);
|
aeb_fate_data:make_integer(-N);
|
||||||
term_to_fate(_GlobEnv, _Env, nil) ->
|
term_to_fate(_GlobEnv, _Env, {nil, _}) ->
|
||||||
aeb_fate_data:make_list([]);
|
aeb_fate_data:make_list([]);
|
||||||
term_to_fate(GlobEnv, Env, {op, '::', [Hd, Tl]}) ->
|
term_to_fate(GlobEnv, Env, {op, _, '::', [Hd, Tl]}) ->
|
||||||
%% The Tl will translate into a list, because FATE lists are just lists
|
%% The Tl will translate into a list, because FATE lists are just lists
|
||||||
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)];
|
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)];
|
||||||
term_to_fate(GlobEnv, Env, {tuple, As}) ->
|
term_to_fate(GlobEnv, Env, {tuple, _, As}) ->
|
||||||
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
|
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
|
||||||
term_to_fate(GlobEnv, Env, {con, Ar, I, As}) ->
|
term_to_fate(GlobEnv, Env, {con, _, Ar, I, As}) ->
|
||||||
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
|
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
|
||||||
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
|
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
|
||||||
term_to_fate(_GlobEnv, _Env, {builtin, bits_all, []}) ->
|
term_to_fate(_GlobEnv, _Env, {builtin, _, bits_all, []}) ->
|
||||||
aeb_fate_data:make_bits(-1);
|
aeb_fate_data:make_bits(-1);
|
||||||
term_to_fate(_GlobEnv, _Env, {builtin, bits_none, []}) ->
|
term_to_fate(_GlobEnv, _Env, {builtin, _, bits_none, []}) ->
|
||||||
aeb_fate_data:make_bits(0);
|
aeb_fate_data:make_bits(0);
|
||||||
term_to_fate(GlobEnv, _Env, {op, bits_set, [B, I]}) ->
|
term_to_fate(GlobEnv, _Env, {op, _, bits_set, [B, I]}) ->
|
||||||
{bits, N} = term_to_fate(GlobEnv, B),
|
{bits, N} = term_to_fate(GlobEnv, B),
|
||||||
J = term_to_fate(GlobEnv, I),
|
J = term_to_fate(GlobEnv, I),
|
||||||
{bits, N bor (1 bsl J)};
|
{bits, N bor (1 bsl J)};
|
||||||
term_to_fate(GlobEnv, _Env, {op, bits_clear, [B, I]}) ->
|
term_to_fate(GlobEnv, _Env, {op, _, bits_clear, [B, I]}) ->
|
||||||
{bits, N} = term_to_fate(GlobEnv, B),
|
{bits, N} = term_to_fate(GlobEnv, B),
|
||||||
J = term_to_fate(GlobEnv, I),
|
J = term_to_fate(GlobEnv, I),
|
||||||
{bits, N band bnot (1 bsl J)};
|
{bits, N band bnot (1 bsl J)};
|
||||||
term_to_fate(GlobEnv, Env, {'let', X, E, Body}) ->
|
term_to_fate(GlobEnv, Env, {'let', _, X, E, Body}) ->
|
||||||
Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) },
|
Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) },
|
||||||
term_to_fate(GlobEnv, Env1, Body);
|
term_to_fate(GlobEnv, Env1, Body);
|
||||||
term_to_fate(_GlobEnv, Env, {var, X}) ->
|
term_to_fate(_GlobEnv, Env, {var, _, X}) ->
|
||||||
case maps:get(X, Env, undefined) of
|
case maps:get(X, Env, undefined) of
|
||||||
undefined -> throw(not_a_fate_value);
|
undefined -> throw(not_a_fate_value);
|
||||||
V -> V
|
V -> V
|
||||||
end;
|
end;
|
||||||
term_to_fate(_GlobEnv, _Env, {builtin, map_empty, []}) ->
|
term_to_fate(_GlobEnv, _Env, {builtin, _, map_empty, []}) ->
|
||||||
aeb_fate_data:make_map(#{});
|
aeb_fate_data:make_map(#{});
|
||||||
term_to_fate(GlobEnv, Env, {op, map_set, [M, K, V]}) ->
|
term_to_fate(GlobEnv, Env, {op, _, map_set, [M, K, V]}) ->
|
||||||
Map = term_to_fate(GlobEnv, Env, M),
|
Map = term_to_fate(GlobEnv, Env, M),
|
||||||
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
|
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
|
||||||
term_to_fate(_GlobEnv, _Env, _) ->
|
term_to_fate(_GlobEnv, _Env, _) ->
|
||||||
@ -313,52 +290,59 @@ term_to_fate(_GlobEnv, _Env, _) ->
|
|||||||
|
|
||||||
to_scode(Env, T) ->
|
to_scode(Env, T) ->
|
||||||
try term_to_fate(Env, T) of
|
try term_to_fate(Env, T) of
|
||||||
V -> [push(?i(V))]
|
V ->
|
||||||
|
FAnn = element(2, T),
|
||||||
|
[dbg_loc(Env, FAnn), push(?i(V))]
|
||||||
catch throw:not_a_fate_value ->
|
catch throw:not_a_fate_value ->
|
||||||
to_scode1(Env, T)
|
to_scode1(Env, T)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
to_scode1(Env, {lit, L}) ->
|
to_scode1(Env, {lit, Ann, L}) ->
|
||||||
[push(?i(lit_to_fate(Env, L)))];
|
[ dbg_loc(Env, Ann), push(?i(lit_to_fate(Env, L))) ];
|
||||||
|
|
||||||
to_scode1(_Env, nil) ->
|
to_scode1(Env, {nil, Ann}) ->
|
||||||
[aeb_fate_ops:nil(?a)];
|
[ dbg_loc(Env, Ann), aeb_fate_ops:nil(?a) ];
|
||||||
|
|
||||||
to_scode1(Env, {var, X}) ->
|
to_scode1(Env, {var, Ann, X}) ->
|
||||||
[push(lookup_var(Env, X))];
|
[ dbg_loc(Env, Ann), push(lookup_var(Env, X)) ];
|
||||||
|
|
||||||
to_scode1(Env, {con, Ar, I, As}) ->
|
to_scode1(Env, {con, Ann, Ar, I, As}) ->
|
||||||
N = length(As),
|
N = length(As),
|
||||||
[[to_scode(notail(Env), A) || A <- As],
|
[ dbg_loc(Env, Ann),
|
||||||
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N))];
|
[to_scode(notail(Env), A) || A <- As],
|
||||||
|
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N)) ];
|
||||||
|
|
||||||
to_scode1(Env, {tuple, As}) ->
|
to_scode1(Env, {tuple, Ann, As}) ->
|
||||||
N = length(As),
|
N = length(As),
|
||||||
[[ to_scode(notail(Env), A) || A <- As ],
|
[ dbg_loc(Env, Ann),
|
||||||
tuple(N)];
|
[ to_scode(notail(Env), A) || A <- As ],
|
||||||
|
tuple(N) ];
|
||||||
|
|
||||||
to_scode1(Env, {proj, E, I}) ->
|
to_scode1(Env, {proj, Ann, E, I}) ->
|
||||||
[to_scode(notail(Env), E),
|
[ dbg_loc(Env, Ann),
|
||||||
aeb_fate_ops:element_op(?a, ?i(I), ?a)];
|
to_scode(notail(Env), E),
|
||||||
|
aeb_fate_ops:element_op(?a, ?i(I), ?a) ];
|
||||||
|
|
||||||
to_scode1(Env, {set_proj, R, I, E}) ->
|
to_scode1(Env, {set_proj, Ann, R, I, E}) ->
|
||||||
[to_scode(notail(Env), E),
|
[ dbg_loc(Env, Ann),
|
||||||
|
to_scode(notail(Env), E),
|
||||||
to_scode(notail(Env), R),
|
to_scode(notail(Env), R),
|
||||||
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a)];
|
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a) ];
|
||||||
|
|
||||||
to_scode1(Env, {op, Op, Args}) ->
|
to_scode1(Env, {op, Ann, Op, Args}) ->
|
||||||
call_to_scode(Env, op_to_scode(Op), Args);
|
[ dbg_loc(Env, Ann) | call_to_scode(Env, op_to_scode(Op), Args) ];
|
||||||
|
|
||||||
to_scode1(Env, {'let', X, {var, Y}, Body}) ->
|
to_scode1(Env, {'let', Ann, X, {var, _, Y}, Body}) ->
|
||||||
Env1 = bind_var(X, lookup_var(Env, Y), Env),
|
Env1 = bind_var(X, lookup_var(Env, Y), Env),
|
||||||
to_scode(Env1, Body);
|
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], to_scode(Env1, Body)) ];
|
||||||
to_scode1(Env, {'let', X, Expr, Body}) ->
|
to_scode1(Env, {'let', Ann, X, Expr, Body}) ->
|
||||||
{I, Env1} = bind_local(X, Env),
|
{I, Env1} = bind_local(X, Env),
|
||||||
[ to_scode(notail(Env), Expr),
|
SCode = [ to_scode(notail(Env), Expr),
|
||||||
aeb_fate_ops:store({var, I}, {stack, 0}),
|
aeb_fate_ops:store({var, I}, {stack, 0}),
|
||||||
to_scode(Env1, Body) ];
|
to_scode(Env1, Body) ],
|
||||||
|
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], SCode) ];
|
||||||
|
|
||||||
to_scode1(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}) ->
|
to_scode1(Env = #env{ current_function = Fun, tailpos = true, debug_info = false }, {def, Ann, Fun, Args}) ->
|
||||||
%% Tail-call to current function, f(e0..en). Compile to
|
%% Tail-call to current function, f(e0..en). Compile to
|
||||||
%% [ let xi = ei ]
|
%% [ let xi = ei ]
|
||||||
%% [ STORE argi xi ]
|
%% [ STORE argi xi ]
|
||||||
@ -371,61 +355,62 @@ to_scode1(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}
|
|||||||
aeb_fate_ops:store({var, I}, ?a)],
|
aeb_fate_ops:store({var, I}, ?a)],
|
||||||
{[I | Is], Acc1, Env2}
|
{[I | Is], Acc1, Env2}
|
||||||
end, {[], [], Env}, Args),
|
end, {[], [], Env}, Args),
|
||||||
[ Code,
|
[ dbg_loc(Env, Ann),
|
||||||
|
Code,
|
||||||
[ aeb_fate_ops:store({arg, I}, {var, J})
|
[ aeb_fate_ops:store({arg, I}, {var, J})
|
||||||
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
|
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
|
||||||
lists:reverse(Vars)) ],
|
lists:reverse(Vars)) ],
|
||||||
loop ];
|
loop ];
|
||||||
to_scode1(Env, {def, Fun, Args}) ->
|
to_scode1(Env, {def, Ann, Fun, Args}) ->
|
||||||
FName = make_function_id(Fun),
|
FName = make_function_id(Fun),
|
||||||
Lbl = aeb_fate_data:make_string(FName),
|
Lbl = aeb_fate_data:make_string(FName),
|
||||||
call_to_scode(Env, local_call(Env, ?i(Lbl)), Args);
|
[ dbg_loc(Env, Ann) | call_to_scode(Env, local_call(Env, ?i(Lbl)), Args) ];
|
||||||
to_scode1(Env, {funcall, Fun, Args}) ->
|
to_scode1(Env, {funcall, Ann, Fun, Args}) ->
|
||||||
call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args);
|
[ dbg_loc(Env, Ann) | call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args) ];
|
||||||
|
|
||||||
to_scode1(Env, {builtin, B, Args}) ->
|
to_scode1(Env, {builtin, Ann, B, Args}) ->
|
||||||
builtin_to_scode(Env, B, Args);
|
[ dbg_loc(Env, Ann) | builtin_to_scode(Env, B, Args) ];
|
||||||
|
|
||||||
to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
|
to_scode1(Env, {remote, Ann, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
|
||||||
Lbl = make_function_id(Fun),
|
Lbl = make_function_id(Fun),
|
||||||
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
||||||
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
||||||
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
||||||
case Protected of
|
SCode = case Protected of
|
||||||
{lit, {bool, false}} ->
|
{lit, _, {bool, false}} ->
|
||||||
case Gas of
|
case Gas of
|
||||||
{builtin, call_gas_left, _} ->
|
{builtin, _, call_gas_left, _} ->
|
||||||
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
||||||
call_to_scode(Env, Call, [Ct, Value | Args]);
|
call_to_scode(Env, Call, [Ct, Value | Args]);
|
||||||
_ ->
|
_ ->
|
||||||
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
|
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
|
||||||
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
||||||
end;
|
end;
|
||||||
{lit, {bool, true}} ->
|
{lit, _, {bool, true}} ->
|
||||||
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
|
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
|
||||||
call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
|
call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
|
||||||
_ ->
|
_ ->
|
||||||
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
|
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
|
||||||
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
|
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
|
||||||
end;
|
end,
|
||||||
|
[ dbg_loc(Env, Ann) | SCode ];
|
||||||
|
|
||||||
to_scode1(_Env, {get_state, Reg}) ->
|
to_scode1(Env, {get_state, Ann, Reg}) ->
|
||||||
[push(?s(Reg))];
|
[ dbg_loc(Env, Ann), push(?s(Reg)) ];
|
||||||
to_scode1(Env, {set_state, Reg, Val}) ->
|
to_scode1(Env, {set_state, Ann, Reg, Val}) ->
|
||||||
call_to_scode(Env, [{'STORE', ?s(Reg), ?a},
|
[ dbg_loc(Env, Ann) | call_to_scode(Env, [{'STORE', ?s(Reg), ?a}, tuple(0)], [Val]) ];
|
||||||
tuple(0)], [Val]);
|
|
||||||
|
|
||||||
to_scode1(Env, {closure, Fun, FVs}) ->
|
to_scode1(Env, {closure, Ann, Fun, FVs}) ->
|
||||||
to_scode(Env, {tuple, [{lit, {string, make_function_id(Fun)}}, FVs]});
|
[ to_scode(Env, {tuple, Ann, [{lit, Ann, {string, make_function_id(Fun)}}, FVs]}) ];
|
||||||
|
|
||||||
to_scode1(Env, {switch, Case}) ->
|
to_scode1(Env, {switch, Ann, Case}) ->
|
||||||
split_to_scode(Env, Case).
|
[ dbg_loc(Env, Ann) | split_to_scode(Env, Case) ].
|
||||||
|
|
||||||
local_call( Env, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun);
|
local_call( Env = #env{debug_info = false}, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun);
|
||||||
local_call(_Env, Fun) -> aeb_fate_ops:call(Fun).
|
local_call(_Env, Fun) -> aeb_fate_ops:call(Fun).
|
||||||
|
|
||||||
split_to_scode(Env, {nosplit, Expr}) ->
|
split_to_scode(Env, {nosplit, Renames, Expr}) ->
|
||||||
[switch_body, to_scode(Env, Expr)];
|
[switch_body, dbg_scoped_vars(Env, Renames, to_scode(Env, Expr))];
|
||||||
split_to_scode(Env, {split, {tuple, _}, X, Alts}) ->
|
split_to_scode(Env, {split, {tuple, _}, X, Alts}) ->
|
||||||
{Def, Alts1} = catchall_to_scode(Env, X, Alts),
|
{Def, Alts1} = catchall_to_scode(Env, X, Alts),
|
||||||
Arg = lookup_var(Env, X),
|
Arg = lookup_var(Env, X),
|
||||||
@ -649,7 +634,7 @@ builtin_to_scode(Env, chain_bytecode_hash, [_Addr] = Args) ->
|
|||||||
builtin_to_scode(Env, chain_clone,
|
builtin_to_scode(Env, chain_clone,
|
||||||
[InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) ->
|
[InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) ->
|
||||||
case GasCap of
|
case GasCap of
|
||||||
{builtin, call_gas_left, _} ->
|
{builtin, _, call_gas_left, _} ->
|
||||||
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
|
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
|
||||||
[Contract, InitArgsT, Value, Prot | InitArgs]
|
[Contract, InitArgsT, Value, Prot | InitArgs]
|
||||||
);
|
);
|
||||||
@ -751,6 +736,77 @@ push(A) -> {'STORE', ?a, A}.
|
|||||||
tuple(0) -> push(?i({tuple, {}}));
|
tuple(0) -> push(?i({tuple, {}}));
|
||||||
tuple(N) -> aeb_fate_ops:tuple(?a, N).
|
tuple(N) -> aeb_fate_ops:tuple(?a, N).
|
||||||
|
|
||||||
|
%% -- Debug info functions --
|
||||||
|
|
||||||
|
dbg_contract(#env{debug_info = false}) ->
|
||||||
|
[];
|
||||||
|
dbg_contract(#env{contract = Contract}) ->
|
||||||
|
[{'DBG_CONTRACT', {immediate, Contract}}].
|
||||||
|
|
||||||
|
dbg_loc(#env{debug_info = false}, _) ->
|
||||||
|
[];
|
||||||
|
dbg_loc(_Env, Ann) ->
|
||||||
|
File = case proplists:get_value(file, Ann, no_file) of
|
||||||
|
no_file -> "";
|
||||||
|
F -> F
|
||||||
|
end,
|
||||||
|
Line = proplists:get_value(line, Ann, undefined),
|
||||||
|
case Line of
|
||||||
|
undefined -> [];
|
||||||
|
_ -> [{'DBG_LOC', {immediate, File}, {immediate, Line}}]
|
||||||
|
end.
|
||||||
|
|
||||||
|
dbg_scoped_vars(#env{debug_info = false}, _, SCode) ->
|
||||||
|
SCode;
|
||||||
|
dbg_scoped_vars(_Env, [], SCode) ->
|
||||||
|
SCode;
|
||||||
|
dbg_scoped_vars(Env, [{SavedVarName, Var} | Rest], SCode) ->
|
||||||
|
dbg_scoped_vars(Env, Rest, dbg_scoped_var(Env, SavedVarName, Var, SCode));
|
||||||
|
dbg_scoped_vars(Env = #env{saved_fresh_names = SavedFreshNames}, [Var | Rest], SCode) ->
|
||||||
|
SavedVarName = maps:get(Var, SavedFreshNames, Var),
|
||||||
|
dbg_scoped_vars(Env, Rest, dbg_scoped_var(Env, SavedVarName, Var, SCode)).
|
||||||
|
|
||||||
|
dbg_scoped_var(Env, SavedVarName, Var, SCode) ->
|
||||||
|
case SavedVarName == "_" orelse is_fresh_name(SavedVarName) of
|
||||||
|
true ->
|
||||||
|
SCode;
|
||||||
|
false ->
|
||||||
|
Register = lookup_var(Env, Var),
|
||||||
|
Def = [{'DBG_DEF', {immediate, SavedVarName}, Register}],
|
||||||
|
Undef = [{'DBG_UNDEF', {immediate, SavedVarName}, Register}],
|
||||||
|
Def ++ dbg_undef(Undef, SCode)
|
||||||
|
end.
|
||||||
|
|
||||||
|
is_fresh_name([$% | _]) ->
|
||||||
|
true;
|
||||||
|
is_fresh_name(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
dbg_undef(_Undef, missing) ->
|
||||||
|
missing;
|
||||||
|
dbg_undef(Undef, loop) ->
|
||||||
|
[Undef, loop];
|
||||||
|
dbg_undef(Undef, switch_body) ->
|
||||||
|
[switch_body, Undef];
|
||||||
|
dbg_undef(Undef, {switch, Arg, Type, Alts, Catch}) ->
|
||||||
|
NewAlts = [ dbg_undef(Undef, Alt) || Alt <- Alts ],
|
||||||
|
NewCatch = dbg_undef(Undef, Catch),
|
||||||
|
NewSwitch = {switch, Arg, Type, NewAlts, NewCatch},
|
||||||
|
NewSwitch;
|
||||||
|
dbg_undef(Undef, SCode) when is_list(SCode) ->
|
||||||
|
lists:droplast(SCode) ++ [dbg_undef(Undef, lists:last(SCode))];
|
||||||
|
dbg_undef(Undef, SCode) when is_tuple(SCode); is_atom(SCode) ->
|
||||||
|
[Mnemonic | _] =
|
||||||
|
case is_tuple(SCode) of
|
||||||
|
true -> tuple_to_list(SCode);
|
||||||
|
false -> [SCode]
|
||||||
|
end,
|
||||||
|
Op = aeb_fate_opcodes:m_to_op(Mnemonic),
|
||||||
|
case aeb_fate_opcodes:end_bb(Op) of
|
||||||
|
true -> [Undef, SCode];
|
||||||
|
false -> [SCode, Undef]
|
||||||
|
end.
|
||||||
|
|
||||||
%% -- Phase II ---------------------------------------------------------------
|
%% -- Phase II ---------------------------------------------------------------
|
||||||
%% Optimize
|
%% Optimize
|
||||||
|
|
||||||
@ -886,6 +942,10 @@ attributes(I) ->
|
|||||||
loop -> Impure(pc, []);
|
loop -> Impure(pc, []);
|
||||||
switch_body -> Pure(none, []);
|
switch_body -> Pure(none, []);
|
||||||
'RETURN' -> Impure(pc, []);
|
'RETURN' -> Impure(pc, []);
|
||||||
|
{'DBG_LOC', _, _} -> Impure(none, []);
|
||||||
|
{'DBG_DEF', _, _} -> Impure(none, []);
|
||||||
|
{'DBG_UNDEF', _, _} -> Impure(none, []);
|
||||||
|
{'DBG_CONTRACT', _} -> Impure(none, []);
|
||||||
{'RETURNR', A} -> Impure(pc, A);
|
{'RETURNR', A} -> Impure(pc, A);
|
||||||
{'CALL', A} -> Impure(?a, [A]);
|
{'CALL', A} -> Impure(?a, [A]);
|
||||||
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
|
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
|
||||||
@ -1605,7 +1665,23 @@ bb(_Name, Code) ->
|
|||||||
Blocks = lists:flatmap(fun split_calls/1, Blocks1),
|
Blocks = lists:flatmap(fun split_calls/1, Blocks1),
|
||||||
Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]),
|
Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]),
|
||||||
BBs = [ set_labels(Labels, B) || B <- Blocks ],
|
BBs = [ set_labels(Labels, B) || B <- Blocks ],
|
||||||
maps:from_list(BBs).
|
maps:from_list(dbg_loc_filter(BBs)).
|
||||||
|
|
||||||
|
%% Filter DBG_LOC instructions to keep one instruction per line
|
||||||
|
dbg_loc_filter(BBs) ->
|
||||||
|
dbg_loc_filter(BBs, [], [], sets:new()).
|
||||||
|
|
||||||
|
dbg_loc_filter([], _, AllBlocks, _) ->
|
||||||
|
lists:reverse(AllBlocks);
|
||||||
|
dbg_loc_filter([{I, []} | Rest], AllOps, AllBlocks, DbgLocs) ->
|
||||||
|
dbg_loc_filter(Rest, [], [{I, lists:reverse(AllOps)} | AllBlocks], DbgLocs);
|
||||||
|
dbg_loc_filter([{I, [Op = {'DBG_LOC', _, _} | Ops]} | Rest], AllOps, AllBlocks, DbgLocs) ->
|
||||||
|
case sets:is_element(Op, DbgLocs) of
|
||||||
|
true -> dbg_loc_filter([{I, Ops} | Rest], AllOps, AllBlocks, DbgLocs);
|
||||||
|
false -> dbg_loc_filter([{I, Ops} | Rest], [Op | AllOps], AllBlocks, sets:add_element(Op, DbgLocs))
|
||||||
|
end;
|
||||||
|
dbg_loc_filter([{I, [Op | Ops]} | Rest], AllOps, AllBlocks, DbgLocs) ->
|
||||||
|
dbg_loc_filter([{I, Ops} | Rest], [Op | AllOps], AllBlocks, DbgLocs).
|
||||||
|
|
||||||
%% -- Break up scode into basic blocks --
|
%% -- Break up scode into basic blocks --
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
-export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]).
|
-export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]).
|
||||||
|
|
||||||
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
-export_type([ann_file/0, ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
||||||
-export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
|
-export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
|
||||||
-export_type([bin_op/0, un_op/0]).
|
-export_type([bin_op/0, un_op/0]).
|
||||||
-export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
|
-export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
|
||||||
@ -24,8 +24,9 @@
|
|||||||
-type ann_col() :: integer().
|
-type ann_col() :: integer().
|
||||||
-type ann_origin() :: system | user.
|
-type ann_origin() :: system | user.
|
||||||
-type ann_format() :: '?:' | hex | infix | prefix | elif.
|
-type ann_format() :: '?:' | hex | infix | prefix | elif.
|
||||||
|
-type ann_file() :: string() | no_file.
|
||||||
|
|
||||||
-type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
|
-type ann() :: [ {file, ann_file()} | {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
|
||||||
| stateful | private | payable | main | interface | entrypoint].
|
| stateful | private | payable | main | interface | entrypoint].
|
||||||
|
|
||||||
-type name() :: string().
|
-type name() :: string().
|
||||||
|
Loading…
x
Reference in New Issue
Block a user