I doubt anyone can help with this question because of the following in Erlang's compile documentation:
Note that the format of assembler files is not documented, and may change between releases - this option is primarily for internal debugging use.
... but just in case, here goes the story's stack trace:
- compile:file/2 with ['S'] to generate the assembly code
- Read .S file and create a key-value data structure with key being the 'function' tuples in the .S file, and value being the function's body, i.e. the assembly instructions which implement the function.
- Modify data structure by adding the assembly to make an external function call in certain functions.
- crash...
Unfortunately I just quickly skimmed the .S files generated when compiling a module containing the following function, with and without the first expression in the function commented out:
spawn_worker(Which) ->
%syner:sync_pt(),
case Which of
?NAIVE -> spawn(err1, naive_worker_loop, [])
end.
When I did that, I thought that the only thing that changed was the tuple:
{call_ext,0,{extfunc,syner,sync_pt,0}}.
... so I assumed that the only thing necessary to inject a function call in the assembly was to add that tuple... but now that I got to actually injecting the tuple... I'm seeing that the assembly generated has some extra instructions:
Without syner:sync_pt():
{function, spawn_worker, 1, 4}.
{label,3}.
{func_info,{atom,err1},{atom,spawn_worker},1}.
{label,4}.
{test,is_eq_exact,{f,5},[{x,0},{atom,naive}]}.
{move,{atom,naive_worker_loop},{x,1}}.
{move,nil,{x,2}}.
{move,{atom,err1},{x,0}}.
{call_ext_only,3,{extfunc,erlang,spawn,3}}.
{label,5}.
{case_end,{x,0}}.
With syner:sync_pt():
{function, spawn_worker, 1, 4}.
{label,3}.
{func_info,{atom,err1},{atom,spawn_worker},1}.
{label,4}.
{allocate,1,1}.
{move,{x,0},{y,0}}.
{call_ext,0,{extfunc,syner,sync_pt,0}}.
{test,is_eq_exact,{f,5},[{y,0},{atom,naive}]}.
{move,{atom,naive_worker_loop},{x,1}}.
{move,nil,{x,2}}.
{move,{atom,err1},{x,0}}.
{call_ext_last,3,{extfunc,erlang,spawn,3},1}.
{label,5}.
{case_end,{y,0}}.
I can't just conclude that adding something like:
{allocate,1,1}.
{move,{x,0},{y,0}}.
{call_ext,0,{extfunc,syner,sync_pt,0}}.
to every function I want to inject an external function call into, will do the trick.
- because I'm not sure if that assembly code applies to all functions I want to inject into (e.g. is {allocate,1,1} always ok)
- because if you take a closer look at the rest of the assembly, it changes slightly (for e.g. {call_ext_only,3,{extfunc,erlang,spawn,3}}. changes to {call_ext_last,3,{extfunc,erlang,spawn,3},1}.).
So now the question is, is there any resource out there I can use to understand and manipulate the assembly generated by Erlang's compile:file/2?
I'm asking this question just in case. I doubt there is a resource for this since the documentation clearly states that there isn't, but I have nothing to lose I guess. Even if there was, it seems like manipulating the assembly code is going to be trickier than I'd like it to be. Using parse_transform/2 is definitely easier, and I did manage to get something similar to work with it... just trying out different alternatives.
Thanks for your time.