1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%=======================================================================
20%% Notes:
21%%   1. It does NOT work for .beam files of previous BEAM versions.
22%%   2. If handling of new BEAM instructions is needed, this should be
23%%      inserted at the end of function resolve_inst().
24%%=======================================================================
25
26-module(beam_disasm).
27
28-export([file/1]). %% the main function
29-export([function__code/1, format_error/1]).
30-ifdef(DEBUG_DISASM).
31-export([dfs/1, df/1, files/1, pp/1, pp/2]).
32-endif.
33
34-author("Kostis Sagonas").
35
36-include("beam_opcodes.hrl").
37-include("beam_disasm.hrl").
38
39%%-----------------------------------------------------------------------
40
41-type index()        :: non_neg_integer().
42-type literals()     :: 'none' | gb_trees:tree(index(), term()).
43-type symbolic_tag() :: 'a' | 'f' | 'h' | 'i' | 'u' | 'x' | 'y' | 'z'.
44-type disasm_tag()   :: symbolic_tag() | 'fr' | 'atom' | 'float' | 'literal'.
45-type disasm_term()  :: 'nil' | {disasm_tag(), _}.
46
47%%-----------------------------------------------------------------------
48
49-define(NO_DEBUG(Str,Xs), ok).
50-define(DEBUG(Str,Xs), io:format(Str,Xs)).
51-define(exit(Reason), exit({?MODULE,?LINE,Reason})).
52
53%%-----------------------------------------------------------------------
54%% Utility functions to get/set their fields. (Uncomment and export
55%% them when/if they get used in other files.)
56%%-----------------------------------------------------------------------
57
58%% -spec function__name(#function{}) -> atom().
59%% function__name(#function{name = N}) -> N.
60%% -spec function__arity(#function{}) -> arity().
61%% function__arity(#function{arity = A}) -> A.
62%% function__entry(#function{entry = E}) -> E.
63
64-spec function__code(#function{}) -> [beam_instr()].
65function__code(#function{code = Code}) -> Code.
66
67-spec function__code_update(#function{}, [beam_instr()]) -> #function{}.
68function__code_update(Function, NewCode) ->
69  Function#function{code = NewCode}.
70
71%%-----------------------------------------------------------------------
72%% Error information
73
74-spec format_error({'internal',term()} | {'error',atom(),term()}) -> string().
75
76format_error({internal,Error}) ->
77    io_lib:format("~p: disassembly failed with reason ~P.",
78		  [?MODULE, Error, 25]);
79format_error({error,Module,Error}) ->
80    lists:flatten(Module:format_error(Error)).
81
82%%-----------------------------------------------------------------------
83%% User comfort functions to directly disassemble to file or to
84%% stream, pretty-printed, and to just pretty-print, also commented.
85%%-----------------------------------------------------------------------
86
87-ifdef(DEBUG_DISASM).
88
89dfs(Files) when is_list(Files) ->
90    lists:foreach(fun df/1, Files).
91
92df(Module) when is_atom(Module) ->
93    case code:which(Module) of
94	File when is_list(File) ->
95	    df(File);
96	Reason when is_atom(Reason) ->
97	    {error,?MODULE,Reason}
98    end;
99df(File) when is_list(File) ->
100    file(File, filename:rootname(File, ".beam")++".dis").
101
102files(Files) when is_list(Files) ->
103    lists:foreach(fun (File) -> file(File, group_leader()) end, Files).
104
105file(File, Dest) ->
106    case file(File) of
107	#beam_file{code = DisasmCode} ->
108	    pp(Dest, [{file,File}, {code,DisasmCode}]);
109	Error -> Error
110    end.
111
112-spec pp([_]) -> 'ok' | {'error', atom()}.
113
114pp(Disasm) ->
115    pp(group_leader(), Disasm).
116
117-spec pp(pid() | file:filename(), [_]) -> 'ok' | {'error', atom()}.
118
119pp(Stream, Disasm) when is_pid(Stream), is_list(Disasm) ->
120    NL = io_lib:nl(),
121    lists:foreach(
122      fun ({code,Code}) ->
123	      lists:foreach(
124		fun (#function{name=F,arity=A,entry=E,code=C}) ->
125			io:format(Stream, "~p.~n", [{function,F,A,E}]),
126			lists:foreach(
127			  fun (I) ->
128				  io:put_chars(Stream, [pp_instr(I)|NL])
129			  end, C),
130			io:nl(Stream)
131		end, Code);
132	  (Item) ->
133	      io:format(Stream, "~p.~n~n", [Item])
134      end, Disasm),
135    ok;
136pp(File, Disasm) when is_list(Disasm) ->
137    case file:open(File, [write]) of
138	{ok,F} ->
139	    Result = pp(F, Disasm),
140	    ok = file:close(F),
141	    Result;
142	{error,_Reason} = Error -> Error
143    end.
144
145pp_instr({comment,I,Comment}) ->
146    [pp_instr(I)|" % "++Comment];
147pp_instr({comment,Comment}) ->
148    ["%% "++Comment];
149pp_instr({label,_}=I) ->
150    io_lib:format("  ~p.", [I]);
151pp_instr(I) ->
152    io_lib:format("    ~p.", [I]).
153
154-endif.
155
156%%-----------------------------------------------------------------------
157%% The main exported function
158%%   File is either a file name or a binary containing the code.
159%%   Call `format_error({error, Module, Reason})' for an error string.
160%%-----------------------------------------------------------------------
161
162-spec file(file:filename() | binary()) -> #beam_file{} | {'error',atom(),_}.
163
164file(File) ->
165    try process_chunks(File)
166    catch error:Reason:Stack ->
167            {error,?MODULE,{internal,{Reason,Stack}}}
168    end.
169
170%%-----------------------------------------------------------------------
171%% Interface might need to be revised -- do not depend on it.
172%%-----------------------------------------------------------------------
173
174process_chunks(F) ->
175    case beam_lib:chunks(F, [atoms,"Code","StrT",
176			     indexed_imports,labeled_exports]) of
177	{ok,{Module,
178	     [{atoms,AtomsList},{"Code",CodeBin},{"StrT",StrBin},
179	      {indexed_imports,ImportsList},{labeled_exports,Exports}]}} ->
180	    Atoms = mk_atoms(AtomsList),
181	    LambdaBin = optional_chunk(F, "FunT"),
182	    Lambdas = beam_disasm_lambdas(LambdaBin, Atoms),
183	    LiteralBin = optional_chunk(F, "LitT"),
184	    Literals = beam_disasm_literals(LiteralBin),
185	    Code = beam_disasm_code(CodeBin, Atoms, mk_imports(ImportsList),
186				    StrBin, Lambdas, Literals, Module),
187	    Attributes =
188		case optional_chunk(F, attributes) of
189		    none -> [];
190		    Atts when is_list(Atts) -> Atts
191		end,
192	    CompInfo =
193		case optional_chunk(F, "CInf") of
194		    none -> [];
195		    CompInfoBin when is_binary(CompInfoBin) ->
196			binary_to_term(CompInfoBin)
197		end,
198	    #beam_file{module = Module,
199		       labeled_exports = Exports,
200		       attributes = Attributes,
201		       compile_info = CompInfo,
202		       code = Code};
203	Error -> Error
204    end.
205
206%%-----------------------------------------------------------------------
207%% Retrieve an optional chunk or return 'none' if the chunk doesn't exist.
208%%-----------------------------------------------------------------------
209
210optional_chunk(F, ChunkTag) ->
211    case beam_lib:chunks(F, [ChunkTag]) of
212	{ok,{_Module,[{ChunkTag,Chunk}]}} -> Chunk;
213	{error,beam_lib,{missing_chunk,_,_}} -> none
214    end.
215
216%%-----------------------------------------------------------------------
217%% Disassembles the lambda (fun) table of a BEAM file.
218%%-----------------------------------------------------------------------
219
220-type l_info() :: {non_neg_integer(), {_,_,_,_,_,_}}.
221-spec beam_disasm_lambdas('none' | binary(), gb_trees:tree(index(), _)) ->
222        'none' | [l_info()].
223
224beam_disasm_lambdas(none, _) -> none;
225beam_disasm_lambdas(<<_:32,Tab/binary>>, Atoms) ->
226    disasm_lambdas(Tab, Atoms, 0).
227
228disasm_lambdas(<<F:32,A:32,Lbl:32,Index:32,NumFree:32,OldUniq:32,More/binary>>,
229	       Atoms, OldIndex) ->
230    Info = {lookup(F, Atoms),A,Lbl,Index,NumFree,OldUniq},
231    [{OldIndex,Info}|disasm_lambdas(More, Atoms, OldIndex+1)];
232disasm_lambdas(<<>>, _, _) -> [].
233
234%%-----------------------------------------------------------------------
235%% Disassembles the literal table (constant pool) of a BEAM file.
236%%-----------------------------------------------------------------------
237
238-spec beam_disasm_literals('none' | binary()) -> literals().
239
240beam_disasm_literals(none) -> none;
241beam_disasm_literals(<<_:32,Compressed/binary>>) ->
242    <<_:32,Tab/binary>> = zlib:uncompress(Compressed),
243    gb_trees:from_orddict(disasm_literals(Tab, 0)).
244
245disasm_literals(<<Sz:32,Ext:Sz/binary,T/binary>>, Index) ->
246    [{Index,binary_to_term(Ext)}|disasm_literals(T, Index+1)];
247disasm_literals(<<>>, _) -> [].
248
249%%-----------------------------------------------------------------------
250%% Disassembles the code chunk of a BEAM file:
251%%   - The code is first disassembled into a long list of instructions.
252%%   - This list is then split into functions and all names are resolved.
253%%-----------------------------------------------------------------------
254
255beam_disasm_code(<<_SS:32, % Sub-Size (length of information before code)
256		  _IS:32,  % Instruction Set Identifier (always 0)
257		  _OM:32,  % Opcode Max
258		  _L:32,_F:32,
259		  CodeBin/binary>>, Atoms, Imports,
260		 Str, Lambdas, Literals, M) ->
261    Code = binary_to_list(CodeBin),
262    try disasm_code(Code, Atoms, Literals) of
263	DisasmCode ->
264	    Functions = get_function_chunks(DisasmCode),
265	    Labels = mk_labels(local_labels(Functions)),
266	    [function__code_update(Function,
267				   resolve_names(Is, Imports, Str,
268						 Labels, Lambdas, Literals, M))
269	     || Function = #function{code=Is} <- Functions]
270    catch
271	error:Rsn ->
272	    ?NO_DEBUG('code disassembling failed: ~p~n', [Rsn]),
273	    ?exit(Rsn)
274    end.
275
276%%-----------------------------------------------------------------------
277
278disasm_code([B|Bs], Atoms, Literals) ->
279    {Instr,RestBs} = disasm_instr(B, Bs, Atoms, Literals),
280    [Instr|disasm_code(RestBs, Atoms, Literals)];
281disasm_code([], _, _) -> [].
282
283%%-----------------------------------------------------------------------
284%% Splits the code stream into chunks representing the code of functions.
285%%
286%% NOTE: code actually looks like
287%%   label L1: ... label Ln:
288%%     func_info ...
289%%   label entry:
290%%     ...
291%%     <on failure, use label Li to show where things died>
292%%     ...
293%% So the labels before each func_info should be included as well.
294%% Ideally, only one such label is needed, but the BEAM compiler
295%% before R8 didn't care to remove the redundant ones.
296%%-----------------------------------------------------------------------
297
298get_function_chunks([]) ->
299    ?exit(empty_code_segment);
300get_function_chunks(Code) ->
301    get_funs(labels_r(Code, [])).
302
303labels_r([], R) -> {R, []};
304labels_r([{label,_}=I|Is], R) ->
305    labels_r(Is, [I|R]);
306labels_r([{line,_}=I|Is], R) ->
307    labels_r(Is, [I|R]);
308labels_r(Is, R) -> {R, Is}.
309
310get_funs({[],[]}) -> [];
311get_funs({_,[]}) ->
312    ?exit(no_func_info_in_code_segment);
313get_funs({LsR0,[{func_info,[{atom,M}=AtomM,{atom,F}=AtomF,ArityArg]}|Code0]})
314  when is_atom(M), is_atom(F) ->
315    Arity = resolve_arg_unsigned(ArityArg),
316    {LsR,Code,RestCode} = get_fun(Code0, []),
317    [{label,[{u,Entry}]}|_] = Code,
318    [#function{name=F,
319	       arity=Arity,
320	       entry=Entry,
321	       code=lists:reverse(LsR0, [{func_info,AtomM,AtomF,Arity}|Code])}
322     |get_funs({LsR,RestCode})].
323
324get_fun([{func_info,_}|_]=Is, R0) ->
325    {LsR,R} = labels_r(R0, []),
326    {LsR,lists:reverse(R),Is};
327get_fun([{int_code_end,[]}], R) ->
328    {[],lists:reverse(R),[]};
329get_fun([I|Is], R) ->
330    get_fun(Is, [I|R]);
331get_fun([], R) ->
332    ?DEBUG('warning: code segment did not end with int_code_end~n',[]),
333    {[],lists:reverse(R),[]}.
334
335%%-----------------------------------------------------------------------
336%% Collects local labels -- I am not sure this is 100% what is needed.
337%%-----------------------------------------------------------------------
338
339local_labels(Funs) ->
340    lists:sort(lists:foldl(fun (F, R) ->
341				   local_labels_1(function__code(F), R)
342			   end, [], Funs)).
343
344local_labels_1(Code0, R) ->
345    Code1 = lists:dropwhile(fun({label,_}) -> true;
346			       ({line,_}) -> true;
347			       ({func_info,_,_,_}) -> false
348			    end, Code0),
349    [{func_info,{atom,M},{atom,F},A}|Code] = Code1,
350    local_labels_2(Code, R, {M,F,A}).
351
352local_labels_2([{label,[{u,L}]}|Code], R, MFA) ->
353    local_labels_2(Code, [{L,MFA}|R], MFA);
354local_labels_2(_, R, _) -> R.
355
356%%-----------------------------------------------------------------------
357%% Disassembles a single BEAM instruction; most instructions are handled
358%% in a generic way; indexing instructions are handled separately.
359%%-----------------------------------------------------------------------
360
361disasm_instr(B, Bs, Atoms, Literals) ->
362    {SymOp, Arity} = beam_opcodes:opname(B),
363    case SymOp of
364	select_val ->
365	    disasm_select_inst(select_val, Bs, Atoms, Literals);
366	select_tuple_arity ->
367	    disasm_select_inst(select_tuple_arity, Bs, Atoms, Literals);
368	put_map_assoc ->
369	    disasm_map_inst(put_map_assoc, Arity, Bs, Atoms, Literals);
370	put_map_exact ->
371	    disasm_map_inst(put_map_exact, Arity, Bs, Atoms, Literals);
372	get_map_elements ->
373	    disasm_map_inst(get_map_elements, Arity, Bs, Atoms, Literals);
374	has_map_fields ->
375	    disasm_map_inst(has_map_fields, Arity, Bs, Atoms, Literals);
376	put_tuple2 ->
377	    disasm_put_tuple2(Bs, Atoms, Literals);
378	make_fun3 ->
379	    disasm_make_fun3(Bs, Atoms, Literals);
380	init_yregs ->
381	    disasm_init_yregs(Bs, Atoms, Literals);
382	_ ->
383	    try decode_n_args(Arity, Bs, Atoms, Literals) of
384		{Args, RestBs} ->
385		    ?NO_DEBUG("instr ~p~n", [{SymOp, Args}]),
386		    {{SymOp, Args}, RestBs}
387	    catch
388		error:Rsn ->
389		    ?NO_DEBUG("decode_n_args(~p,~p) failed~n", [Arity, Bs]),
390		    ?exit({cannot_disasm_instr, {SymOp, Arity, Rsn}})
391	    end
392    end.
393
394%%-----------------------------------------------------------------------
395%% Disassembles a BEAM select_* instruction used for indexing.
396%%   Currently handles {select_val,3} and {select_tuple_arity,3} insts.
397%%
398%%   The arguments of a "select"-type instruction look as follows:
399%%       <reg>, {f,FailLabel}, {list, <num cases>, [<case1> ... <caseN>]}
400%%   where each case is of the form [symbol,{f,Label}].
401%%-----------------------------------------------------------------------
402
403disasm_select_inst(Inst, Bs, Atoms, Literals) ->
404    {X, Bs1} = decode_arg(Bs, Atoms, Literals),
405    {F, Bs2} = decode_arg(Bs1, Atoms, Literals),
406    {Z, Bs3} = decode_arg(Bs2, Atoms, Literals),
407    {U, Bs4} = decode_arg(Bs3, Atoms, Literals),
408    {u, Len} = U,
409    {List, RestBs} = decode_n_args(Len, Bs4, Atoms, Literals),
410    {{Inst, [X,F,{Z,U,List}]}, RestBs}.
411
412disasm_map_inst(Inst, Arity, Bs0, Atoms, Literals) ->
413    {Args0,Bs1} = decode_n_args(Arity, Bs0, Atoms, Literals),
414    %% no droplast ..
415    [Z|Args1]  = lists:reverse(Args0),
416    Args       = lists:reverse(Args1),
417    {U, Bs2}   = decode_arg(Bs1, Atoms, Literals),
418    {u, Len}   = U,
419    {List, RestBs} = decode_n_args(Len, Bs2, Atoms, Literals),
420    {{Inst, Args ++ [{Z,U,List}]}, RestBs}.
421
422disasm_put_tuple2(Bs, Atoms, Literals) ->
423    {X, Bs1} = decode_arg(Bs, Atoms, Literals),
424    {Z, Bs2} = decode_arg(Bs1, Atoms, Literals),
425    {U, Bs3} = decode_arg(Bs2, Atoms, Literals),
426    {u, Len} = U,
427    {List, RestBs} = decode_n_args(Len, Bs3, Atoms, Literals),
428    {{put_tuple2, [X,{Z,U,List}]}, RestBs}.
429
430disasm_make_fun3(Bs, Atoms, Literals) ->
431    {Fun, Bs1} = decode_arg(Bs, Atoms, Literals),
432    {Dst, Bs2} = decode_arg(Bs1, Atoms, Literals),
433    {Z, Bs3} = decode_arg(Bs2, Atoms, Literals),
434    {U, Bs4} = decode_arg(Bs3, Atoms, Literals),
435    {u, Len} = U,
436    {List, RestBs} = decode_n_args(Len, Bs4, Atoms, Literals),
437    {{make_fun3, [Fun,Dst,{Z,U,List}]}, RestBs}.
438
439disasm_init_yregs(Bs1, Atoms, Literals) ->
440    {Z, Bs2} = decode_arg(Bs1, Atoms, Literals),
441    {U, Bs3} = decode_arg(Bs2, Atoms, Literals),
442    {u, Len} = U,
443    {List, RestBs} = decode_n_args(Len, Bs3, Atoms, Literals),
444    {{init_yregs, [{Z,U,List}]}, RestBs}.
445
446%%-----------------------------------------------------------------------
447%% decode_arg([Byte]) -> {Arg, [Byte]}
448%%
449%% - an arg can have variable length, so we must return arg + remaining bytes
450%% - decodes an argument into its 'raw' form: { Tag, Value }
451%%   several types map to a single tag, so the byte code instr must then
452%%   assign a type to it
453%%-----------------------------------------------------------------------
454
455-spec decode_arg([byte(),...]) -> {{disasm_tag(),_}, [byte()]}.
456
457decode_arg([B|Bs]) ->
458    Tag = decode_tag(B band 2#111),
459    ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n', [Tag, B, Bs]),
460    case Tag of
461	z ->
462	    decode_z_tagged(Tag, B, Bs, no_literals);
463	_ ->
464	    %% all other cases are handled as if they were integers
465	    decode_int(Tag, B, Bs)
466    end.
467
468-spec decode_arg([byte(),...], gb_trees:tree(index(), _), literals()) ->
469        {disasm_term(), [byte()]}.
470
471decode_arg([B|Bs0], Atoms, Literals) ->
472    Tag = decode_tag(B band 2#111),
473    ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n', [Tag, B, Bs0]),
474    case Tag of
475	z ->
476	    decode_z_tagged(Tag, B, Bs0, Literals);
477	a ->
478	    %% atom or nil
479	    case decode_int(Tag, B, Bs0) of
480		{{a,0},Bs} -> {nil,Bs};
481		{{a,I},Bs} -> {{atom,lookup(I, Atoms)},Bs}
482	    end;
483	_ ->
484	    %% all other cases are handled as if they were integers
485	    decode_int(Tag, B, Bs0)
486    end.
487
488%%-----------------------------------------------------------------------
489%% Decodes an integer value.  Handles positives, negatives, and bignums.
490%%
491%% Tries to do the opposite of:
492%%   beam_asm:encode(1, 5) =            [81]
493%%   beam_asm:encode(1, 1000) =         [105,232]
494%%   beam_asm:encode(1, 2047) =         [233,255]
495%%   beam_asm:encode(1, 2048) =         [25,8,0]
496%%   beam_asm:encode(1,-1) =            [25,255,255]
497%%   beam_asm:encode(1,-4294967295) =   [121,255,0,0,0,1]
498%%   beam_asm:encode(1, 4294967295) =   [121,0,255,255,255,255]
499%%   beam_asm:encode(1, 429496729501) = [121,99,255,255,255,157]
500%%-----------------------------------------------------------------------
501
502decode_int(Tag,B,Bs) when (B band 16#08) =:= 0 ->
503    %% N < 16 = 4 bits, NNNN:0:TTT
504    N = B bsr 4,
505    {{Tag,N},Bs};
506decode_int(Tag,B,Bs) when (B band 16#10) =:= 0 ->
507    %% N < 2048 = 11 bits = 3:8 bits, NNN:01:TTT, NNNNNNNN
508    [B1|Bs1] = Bs,
509    Val0 = B band 2#11100000,
510    N = (Val0 bsl 3) bor B1,
511    ?NO_DEBUG('NNN:01:TTT, NNNNNNNN = ~n~p:01:~p, ~p = ~p~n', [Val0,Tag,B,N]),
512    {{Tag,N},Bs1};
513decode_int(Tag,B,Bs) ->
514    {Len,Bs1} = decode_int_length(B,Bs),
515    {IntBs,RemBs} = take_bytes(Len,Bs1),
516    N = build_arg(IntBs),
517    [F|_] = IntBs,
518    Num = if F > 127, Tag =:= i -> decode_negative(N,Len);
519	     true -> N
520	  end,
521    ?NO_DEBUG('Len = ~p, IntBs = ~p, Num = ~p~n', [Len,IntBs,Num]),
522    {{Tag,Num},RemBs}.
523
524-spec decode_int_length(integer(), [byte()]) -> {integer(), [byte()]}.
525
526decode_int_length(B, Bs) ->
527    %% The following imitates get_erlang_integer() in beam_load.c
528    %% Len is the size of the integer value in bytes
529    case B bsr 5 of
530	7 ->
531	    {Arg,ArgBs} = decode_arg(Bs),
532	    case Arg of
533		{u,L} ->
534		    {L+9,ArgBs};  % 9 stands for 7+2
535		_ ->
536		    ?exit({decode_int,weird_bignum_sublength,Arg})
537	    end;
538	L ->
539	    {L+2,Bs}
540    end.
541
542-spec decode_negative(non_neg_integer(), non_neg_integer()) -> neg_integer().
543
544decode_negative(N, Len) ->
545    N - (1 bsl (Len*8)). % 8 is number of bits in a byte
546
547%%-----------------------------------------------------------------------
548%% Decodes lists and floating point numbers.
549%%-----------------------------------------------------------------------
550
551decode_z_tagged(Tag,B,Bs,Literals) when (B band 16#08) =:= 0 ->
552    N = B bsr 4,
553    case N of
554	0 -> % float
555	    decode_float(Bs);
556	1 -> % list
557	    {{Tag,N},Bs};
558	2 -> % fr
559	    decode_fr(Bs);
560	3 -> % allocation list
561	    decode_alloc_list(Bs, Literals);
562	4 -> % literal
563	    {{u,LitIndex},RestBs} = decode_arg(Bs),
564	    case gb_trees:get(LitIndex, Literals) of
565		Float when is_float(Float) ->
566		    {{float,Float},RestBs};
567		Literal ->
568		    {{literal,Literal},RestBs}
569	    end;
570	_ ->
571	    ?exit({decode_z_tagged,{invalid_extended_tag,N}})
572    end;
573decode_z_tagged(_,B,_,_) ->
574    ?exit({decode_z_tagged,{weird_value,B}}).
575
576-spec decode_float([byte(),...]) -> {{'float', float()}, [byte()]}.
577
578decode_float(Bs) ->
579    {FL,RestBs} = take_bytes(8,Bs),
580    <<Float:64/float>> = list_to_binary(FL),
581    {{float,Float},RestBs}.
582
583-spec decode_fr([byte(),...]) -> {{'fr', non_neg_integer()}, [byte()]}.
584
585decode_fr(Bs) ->
586    {{u,Fr},RestBs} = decode_arg(Bs),
587    {{fr,Fr},RestBs}.
588
589decode_alloc_list(Bs, Literals) ->
590    {{u,N},RestBs} = decode_arg(Bs),
591    decode_alloc_list_1(N, Literals, RestBs, []).
592
593decode_alloc_list_1(0, _Literals, RestBs, Acc) ->
594    {{u,{alloc,lists:reverse(Acc)}},RestBs};
595decode_alloc_list_1(N, Literals, Bs0, Acc) ->
596    {{u,Type},Bs1} = decode_arg(Bs0),
597    {{u,Val},Bs} = decode_arg(Bs1),
598    Res = case Type of
599	      0 -> {words,Val};
600	      1 -> {floats,Val};
601              2 -> {funs,Val}
602	  end,
603    decode_alloc_list_1(N-1, Literals, Bs, [Res|Acc]).
604
605%%-----------------------------------------------------------------------
606%% take N bytes from a stream, return {Taken_bytes, Remaining_bytes}
607%%-----------------------------------------------------------------------
608
609-spec take_bytes(non_neg_integer(), [byte()]) -> {[byte()], [byte()]}.
610
611take_bytes(N, Bs) ->
612    take_bytes(N, Bs, []).
613
614take_bytes(N, [B|Bs], Acc) when N > 0 ->
615    take_bytes(N-1, Bs, [B|Acc]);
616take_bytes(0, Bs, Acc) ->
617    {lists:reverse(Acc), Bs}.
618
619%%-----------------------------------------------------------------------
620%% from a list of bytes Bn,Bn-1,...,B1,B0
621%% build  (Bn << 8*n) bor ... bor (B1 << 8) bor (B0 << 0)
622%%-----------------------------------------------------------------------
623
624build_arg(Bs) ->
625    build_arg(Bs, 0).
626
627build_arg([B|Bs], N) ->
628    build_arg(Bs, (N bsl 8) bor B);
629build_arg([], N) ->
630    N.
631
632%%-----------------------------------------------------------------------
633%% Decodes a bunch of arguments and returns them in a list
634%%-----------------------------------------------------------------------
635
636decode_n_args(N, Bs, Atoms, Literals) when N >= 0 ->
637    decode_n_args(N, [], Bs, Atoms, Literals).
638
639decode_n_args(N, Acc, Bs0, Atoms, Literals) when N > 0 ->
640    {A1,Bs} = decode_arg(Bs0, Atoms, Literals),
641    decode_n_args(N-1, [A1|Acc], Bs, Atoms, Literals);
642decode_n_args(0, Acc, Bs, _, _) ->
643    {lists:reverse(Acc),Bs}.
644
645%%-----------------------------------------------------------------------
646%% Convert a numeric tag value into a symbolic one
647%%-----------------------------------------------------------------------
648
649-spec decode_tag(0..7) -> symbolic_tag().
650
651decode_tag(?tag_u) -> u;
652decode_tag(?tag_i) -> i;
653decode_tag(?tag_a) -> a;
654decode_tag(?tag_x) -> x;
655decode_tag(?tag_y) -> y;
656decode_tag(?tag_f) -> f;
657decode_tag(?tag_h) -> h;
658decode_tag(?tag_z) -> z.
659
660%%-----------------------------------------------------------------------
661%% - replace all references {a,I} with the atom with index I (or {atom,A})
662%% - replace all references to {i,K} in an external call position with
663%%    the proper MFA (position in list, first elt = 0, yields MFA to use)
664%% - resolve strings, represented as <offset, length>, into their
665%%   actual values by using string table
666%%    (note: string table should be passed as a BINARY so that we can
667%%    use binary_to_list/3!)
668%% - convert instruction to its readable form ...
669%%
670%% Currently, only the first three are done (systematically, at least).
671%%
672%% Note: It MAY be premature to remove the lists of args, since that
673%%  representation means it is simpler to iterate over all args, etc.
674%%-----------------------------------------------------------------------
675
676resolve_names(Fun, Imports, Str, Lbls, Lambdas, Literals, M) ->
677    [resolve_inst(Instr, Imports, Str, Lbls, Lambdas, Literals, M) || Instr <- Fun].
678
679%%
680%% New make_fun2/4 instruction added in August 2001 (R8).
681%% We handle it specially here to avoid adding an argument to
682%% the clause for every instruction.
683%%
684
685resolve_inst({make_fun2,Args}, _, _, _, Lambdas, _, M) ->
686    [OldIndex] = resolve_args(Args),
687    {OldIndex,{F,A,_Lbl,_Index,NumFree,OldUniq}} =
688	lists:keyfind(OldIndex, 1, Lambdas),
689    {make_fun2,{M,F,A},OldIndex,OldUniq,NumFree};
690resolve_inst({make_fun3,[Fun,Dst,{{z,1},{u,_},Env0}]}, _, _, _, Lambdas, _, M) ->
691    OldIndex = resolve_arg(Fun),
692    Env1 = resolve_args(Env0),
693    {OldIndex,{F,A,_Lbl,_Index,_NumFree,OldUniq}} =
694	lists:keyfind(OldIndex, 1, Lambdas),
695    {make_fun3,{M,F,A},OldIndex,OldUniq,Dst,{list,Env1}};
696resolve_inst(Instr, Imports, Str, Lbls, _Lambdas, _Literals, _M) ->
697    %% io:format(?MODULE_STRING":resolve_inst ~p.~n", [Instr]),
698    resolve_inst(Instr, Imports, Str, Lbls).
699
700resolve_inst({label,[{u,L}]},_,_,_) ->
701    {label,L};
702resolve_inst(FuncInfo,_,_,_) when element(1, FuncInfo) =:= func_info ->
703    FuncInfo; % already resolved
704%% resolve_inst(int_code_end,_,_,_,_) ->  % instruction already handled
705%%    int_code_end;                       % should not really be handled here
706resolve_inst({call,[{u,N},{f,L}]},_,_,Lbls) ->
707    {call,N,lookup(L,Lbls)};
708resolve_inst({call_last,[{u,N},{f,L},{u,U}]},_,_,Lbls) ->
709    {call_last,N,lookup(L,Lbls),U};
710resolve_inst({call_only,[{u,N},{f,L}]},_,_,Lbls) ->
711    {call_only,N,lookup(L,Lbls)};
712resolve_inst({call_ext,[{u,N},{u,MFAix}]},Imports,_,_) ->
713    {call_ext,N,lookup(MFAix+1,Imports)};
714resolve_inst({call_ext_last,[{u,N},{u,MFAix},{u,X}]},Imports,_,_) ->
715    {call_ext_last,N,lookup(MFAix+1,Imports),X};
716resolve_inst({bif0,Args},Imports,_,_) ->
717    [Bif,Reg] = resolve_args(Args),
718    {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
719    {bif,BifName,nofail,[],Reg};
720resolve_inst({bif1,Args},Imports,_,_) ->
721    [F,Bif,A1,Reg] = resolve_args(Args),
722    {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
723    {bif,BifName,F,[A1],Reg};
724resolve_inst({bif2,Args},Imports,_,_) ->
725    [F,Bif,A1,A2,Reg] = resolve_args(Args),
726    {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
727    {bif,BifName,F,[A1,A2],Reg};
728resolve_inst({allocate,[{u,X0},{u,X1}]},_,_,_) ->
729    {allocate,X0,X1};
730resolve_inst({allocate_heap,[{u,X0},{u,X1},{u,X2}]},_,_,_) ->
731    {allocate_heap,X0,X1,X2};
732resolve_inst({allocate_zero,[{u,X0},{u,X1}]},_,_,_) ->
733    {allocate_zero,X0,X1};
734resolve_inst({allocate_heap_zero,[{u,X0},{u,X1},{u,X2}]},_,_,_) ->
735    {allocate_heap_zero,X0,X1,X2};
736resolve_inst({test_heap,[{u,X0},{u,X1}]},_,_,_) ->
737    {test_heap,X0,X1};
738resolve_inst({init,[Dst]},_,_,_) ->
739    {init,Dst};
740resolve_inst({deallocate,[{u,L}]},_,_,_) ->
741    {deallocate,L};
742resolve_inst({return,[]},_,_,_) ->
743    return;
744resolve_inst({send,[]},_,_,_) ->
745    send;
746resolve_inst({remove_message,[]},_,_,_) ->
747    remove_message;
748resolve_inst({timeout,[]},_,_,_) ->
749    timeout;
750resolve_inst({loop_rec,[Lbl,Dst]},_,_,_) ->
751    {loop_rec,Lbl,Dst};
752resolve_inst({loop_rec_end,[Lbl]},_,_,_) ->
753    {loop_rec_end,Lbl};
754resolve_inst({wait,[Lbl]},_,_,_) ->
755    {wait,Lbl};
756resolve_inst({wait_timeout,[Lbl,Int]},_,_,_) ->
757    {wait_timeout,Lbl,resolve_arg(Int)};
758resolve_inst({is_lt=I,Args0},_,_,_) ->
759    [L|Args] = resolve_args(Args0),
760    {test,I,L,Args};
761resolve_inst({is_ge=I,Args0},_,_,_) ->
762    [L|Args] = resolve_args(Args0),
763    {test,I,L,Args};
764resolve_inst({is_eq=I,Args0},_,_,_) ->
765    [L|Args] = resolve_args(Args0),
766    {test,I,L,Args};
767resolve_inst({is_ne=I,Args0},_,_,_) ->
768    [L|Args] = resolve_args(Args0),
769    {test,I,L,Args};
770resolve_inst({is_eq_exact=I,Args0},_,_,_) ->
771    [L|Args] = resolve_args(Args0),
772    {test,I,L,Args};
773resolve_inst({is_ne_exact=I,Args0},_,_,_) ->
774    [L|Args] = resolve_args(Args0),
775    {test,I,L,Args};
776resolve_inst({is_integer=I,Args0},_,_,_) ->
777    [L|Args] = resolve_args(Args0),
778    {test,I,L,Args};
779resolve_inst({is_float=I,Args0},_,_,_) ->
780    [L|Args] = resolve_args(Args0),
781    {test,I,L,Args};
782resolve_inst({is_number=I,Args0},_,_,_) ->
783    [L|Args] = resolve_args(Args0),
784    {test,I,L,Args};
785resolve_inst({is_atom=I,Args0},_,_,_) ->
786    [L|Args] = resolve_args(Args0),
787    {test,I,L,Args};
788resolve_inst({is_pid=I,Args0},_,_,_) ->
789    [L|Args] = resolve_args(Args0),
790    {test,I,L,Args};
791resolve_inst({is_reference=I,Args0},_,_,_) ->
792    [L|Args] = resolve_args(Args0),
793    {test,I,L,Args};
794resolve_inst({is_port=I,Args0},_,_,_) ->
795    [L|Args] = resolve_args(Args0),
796    {test,I,L,Args};
797resolve_inst({is_nil=I,Args0},_,_,_) ->
798    [L|Args] = resolve_args(Args0),
799    {test,I,L,Args};
800resolve_inst({is_binary=I,Args0},_,_,_) ->
801    [L|Args] = resolve_args(Args0),
802    {test,I,L,Args};
803resolve_inst({is_list=I,Args0},_,_,_) ->
804    [L|Args] = resolve_args(Args0),
805    {test,I,L,Args};
806resolve_inst({is_nonempty_list=I,Args0},_,_,_) ->
807    [L|Args] = resolve_args(Args0),
808    {test,I,L,Args};
809resolve_inst({is_tuple=I,Args0},_,_,_) ->
810    [L|Args] = resolve_args(Args0),
811    {test,I,L,Args};
812resolve_inst({test_arity=I,Args0},_,_,_) ->
813    [L|Args] = resolve_args(Args0),
814    {test,I,L,Args};
815resolve_inst({is_tagged_tuple=I,Args0},_,_,_) ->
816    [F|Args] = resolve_args(Args0),
817    {test,I,F,Args};
818resolve_inst({select_val,Args},_,_,_) ->
819    [Reg,FLbl,{{z,1},{u,_Len},List0}] = Args,
820    List = resolve_args(List0),
821    {select_val,Reg,FLbl,{list,List}};
822resolve_inst({select_tuple_arity,Args},_,_,_) ->
823    [Reg,FLbl,{{z,1},{u,_Len},List0}] = Args,
824    List = resolve_args(List0),
825    {select_tuple_arity,Reg,FLbl,{list,List}};
826resolve_inst({jump,[Lbl]},_,_,_) ->
827    {jump,Lbl};
828resolve_inst({'catch',[Dst,Lbl]},_,_,_) ->
829    {'catch',Dst,Lbl};
830resolve_inst({catch_end,[Dst]},_,_,_) ->
831    {catch_end,Dst};
832resolve_inst({move,[Src,Dst]},_,_,_) ->
833    {move,resolve_arg(Src),Dst};
834resolve_inst({get_list,[Src,Dst1,Dst2]},_,_,_) ->
835    {get_list,Src,Dst1,Dst2};
836resolve_inst({get_tuple_element,[Src,{u,Off},Dst]},_,_,_) ->
837    {get_tuple_element,resolve_arg(Src),Off,resolve_arg(Dst)};
838resolve_inst({set_tuple_element,[Src,Dst,{u,Off}]},_,_,_) ->
839    {set_tuple_element,resolve_arg(Src),resolve_arg(Dst),Off};
840resolve_inst({put_list,[Src1,Src2,Dst]},_,_,_) ->
841    {put_list,resolve_arg(Src1),resolve_arg(Src2),Dst};
842resolve_inst({put_tuple,[{u,Arity},Dst]},_,_,_) ->
843    {put_tuple,Arity,Dst};
844resolve_inst({put,[Src]},_,_,_) ->
845    {put,resolve_arg(Src)};
846resolve_inst({badmatch,[X]},_,_,_) ->
847    {badmatch,resolve_arg(X)};
848resolve_inst({if_end,[]},_,_,_) ->
849    if_end;
850resolve_inst({case_end,[X]},_,_,_) ->
851    {case_end,resolve_arg(X)};
852resolve_inst({call_fun,[{u,N}]},_,_,_) ->
853    {call_fun,N};
854resolve_inst({is_function=I,Args0},_,_,_) ->
855    [L|Args] = resolve_args(Args0),
856    {test,I,L,Args};
857resolve_inst({call_ext_only,[{u,N},{u,MFAix}]},Imports,_,_) ->
858    {call_ext_only,N,lookup(MFAix+1,Imports)};
859%%
860%% Instructions for handling binaries added in R7A & R7B
861%%
862resolve_inst({bs_put_integer,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
863    [A2,A5] = resolve_args([Arg2,Arg5]),
864    {bs_put_integer,Lbl,A2,N,decode_field_flags(U),A5};
865resolve_inst({bs_put_binary,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
866    [A2,A5] = resolve_args([Arg2,Arg5]),
867    {bs_put_binary,Lbl,A2,N,decode_field_flags(U),A5};
868resolve_inst({bs_put_float,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
869    [A2,A5] = resolve_args([Arg2,Arg5]),
870    {bs_put_float,Lbl,A2,N,decode_field_flags(U),A5};
871resolve_inst({bs_put_string,[{u,Len},{u,Off}]},_,Strings,_) ->
872    String = if Len > 0 -> binary_to_list(Strings, Off+1, Off+Len);
873		true -> ""
874	     end,
875    {bs_put_string,Len,{string,String}};
876
877%%
878%% Instructions for handling floating point numbers added in June 2001 (R8).
879%%
880resolve_inst({fclearerror,[]},_,_,_) ->
881    fclearerror;
882resolve_inst({fcheckerror,[Arg]},_,_,_) ->
883    {fcheckerror,resolve_arg(Arg)};
884resolve_inst({fmove,Args},_,_,_) ->
885    [FR,Reg] = resolve_args(Args),
886    {fmove,FR,Reg};
887resolve_inst({fconv,Args},_,_,_) ->
888    [Reg,FR] = resolve_args(Args),
889    {fconv,Reg,FR};
890resolve_inst({fadd=I,Args},_,_,_) ->
891    [F,A1,A2,Reg] = resolve_args(Args),
892    {arithfbif,I,F,[A1,A2],Reg};
893resolve_inst({fsub=I,Args},_,_,_) ->
894    [F,A1,A2,Reg] = resolve_args(Args),
895    {arithfbif,I,F,[A1,A2],Reg};
896resolve_inst({fmul=I,Args},_,_,_) ->
897    [F,A1,A2,Reg] = resolve_args(Args),
898    {arithfbif,I,F,[A1,A2],Reg};
899resolve_inst({fdiv=I,Args},_,_,_) ->
900    [F,A1,A2,Reg] = resolve_args(Args),
901    {arithfbif,I,F,[A1,A2],Reg};
902resolve_inst({fnegate,Args},_,_,_) ->
903    [F,Arg,Reg] = resolve_args(Args),
904    {arithfbif,fnegate,F,[Arg],Reg};
905
906%%
907%% Instructions for try expressions added in January 2003 (R10).
908%%
909resolve_inst({'try',[Reg,Lbl]},_,_,_) -> % analogous to 'catch'
910    {'try',Reg,Lbl};
911resolve_inst({try_end,[Reg]},_,_,_) ->   % analogous to 'catch_end'
912    {try_end,Reg};
913resolve_inst({try_case,[Reg]},_,_,_) ->  % analogous to 'catch_end'
914    {try_case,Reg};
915resolve_inst({try_case_end,[Arg]},_,_,_) ->
916    {try_case_end,resolve_arg(Arg)};
917resolve_inst({raise,[_Reg1,_Reg2]=Regs},_,_,_) ->
918    {raise,{f,0},Regs,{x,0}};		 % do NOT wrap this as a 'bif'
919					 % as there is no raise/2 bif!
920
921%%
922%% New bit syntax instructions added in February 2004 (R10B).
923%%
924resolve_inst({bs_init2,[Lbl,Arg2,{u,W},{u,R},{u,F},Arg6]},_,_,_) ->
925    [A2,A6] = resolve_args([Arg2,Arg6]),
926    {bs_init2,Lbl,A2,W,R,decode_field_flags(F),A6};
927resolve_inst({bs_add=I,[Lbl,Arg2,Arg3,Arg4,Arg5]},_,_,_) ->
928    [A2,A3,A4,A5] = resolve_args([Arg2,Arg3,Arg4,Arg5]),
929    {I,Lbl,[A2,A3,A4],A5};
930
931%%
932%% New apply instructions added in April 2004 (R10B).
933%%
934resolve_inst({apply,[{u,Arity}]},_,_,_) ->
935    {apply,Arity};
936resolve_inst({apply_last,[{u,Arity},{u,D}]},_,_,_) ->
937    {apply_last,Arity,D};
938
939%%
940%% New test instruction added in April 2004 (R10B).
941%%
942resolve_inst({is_boolean=I,Args0},_,_,_) ->
943    [L|Args] = resolve_args(Args0),
944    {test,I,L,Args};
945
946%%
947%% New instruction added in June 2005.
948%%
949resolve_inst({is_function2=I,Args0},_,_,_) ->
950    [L|Args] = resolve_args(Args0),
951    {test,I,L,Args};
952
953%%
954%% New bit syntax matching added in Dec 2005 (R11B).
955%%
956resolve_inst({bs_start_match2=I,[F,Reg,{u,Live},{u,Max},Ms]},_,_,_) ->
957    {test,I,F,[Reg,Live,Max,Ms]};
958resolve_inst({bs_get_integer2=I,[Lbl,Ms,{u,Live},Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
959    [A2,A5] = resolve_args([Arg2,Arg5]),
960    {test,I,Lbl,[Ms, Live,A2,N,decode_field_flags(U),A5]};
961resolve_inst({bs_get_binary2=I,[Lbl,Ms,{u,Live},Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
962    [A2,A5] = resolve_args([Arg2,Arg5]),
963    {test,I,Lbl,[Ms, Live,A2,N,decode_field_flags(U),A5]};
964resolve_inst({bs_get_float2=I,[Lbl,Ms,{u,Live},Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
965    [A2,A5] = resolve_args([Arg2,Arg5]),
966    {test,I,Lbl,[Ms, Live,A2,N,decode_field_flags(U),A5]};
967resolve_inst({bs_skip_bits2=I,[Lbl,Ms,Arg2,{u,N},{u,U}]},_,_,_) ->
968    A2 = resolve_arg(Arg2),
969    {test,I,Lbl,[Ms,A2,N,decode_field_flags(U)]};
970resolve_inst({bs_test_tail2=I,[F,Ms,{u,N}]},_,_,_) ->
971    {test,I,F,[Ms,N]};
972resolve_inst({bs_save2=I,[Ms,{u,N}]},_,_,_) ->
973    {I,Ms,N};
974resolve_inst({bs_restore2=I,[Ms,{u,N}]},_,_,_) ->
975    {I,Ms,N};
976resolve_inst({bs_save2=I,[Ms,{atom,_}=Atom]},_,_,_) ->
977    %% New operand type in R12B.
978    {I,Ms,Atom};
979resolve_inst({bs_restore2=I,[Ms,{atom,_}=Atom]},_,_,_) ->
980    %% New operand type in R12B.
981    {I,Ms,Atom};
982
983%%
984%% New instructions for guard BIFs that may GC. Added in Jan 2006 (R11B).
985%%
986resolve_inst({gc_bif1,Args},Imports,_,_) ->
987    [F,Live,Bif,A1,Reg] = resolve_args(Args),
988    {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
989    {gc_bif,BifName,F,Live,[A1],Reg};
990resolve_inst({gc_bif2,Args},Imports,_,_) ->
991    [F,Live,Bif,A1,A2,Reg] = resolve_args(Args),
992    {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
993    {gc_bif,BifName,F,Live,[A1,A2],Reg};
994
995%%
996%% New instruction in R14, gc_bif with 3 arguments
997%%
998resolve_inst({gc_bif3,Args},Imports,_,_) ->
999    [F,Live,Bif,A1,A2,A3,Reg] = resolve_args(Args),
1000    {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
1001    {gc_bif,BifName,F,Live,[A1,A2,A3],Reg};
1002
1003%%
1004%% R11B-5.
1005%%
1006resolve_inst({is_bitstr=I,Args0},_,_,_) ->
1007    [L|Args] = resolve_args(Args0),
1008    {test,I,L,Args};
1009
1010%%
1011%% R12B.
1012%%
1013resolve_inst({bs_context_to_binary=I,[Reg0]},_,_,_) ->
1014    Reg = resolve_arg(Reg0),
1015    {I,Reg};
1016resolve_inst({bs_test_unit=I,[F,Ms,{u,N}]},_,_,_) ->
1017    {test,I,F,[Ms,N]};
1018resolve_inst({bs_match_string=I,[F,Ms,{u,Bits},{u,Off}]},_,Strings,_) ->
1019    Len = (Bits+7) div 8,
1020    String = if
1021		 Len > 0 ->
1022		     <<_:Off/binary,Bin:Len/binary,_/binary>> = Strings,
1023		     Bin;
1024		 true -> <<>>
1025	     end,
1026    {test,I,F,[Ms,Bits,String]};
1027resolve_inst({bs_init_writable=I,[]},_,_,_) ->
1028    I;
1029resolve_inst({bs_append=I,[Lbl,Arg2,{u,W},{u,R},{u,U},Arg6,{u,F},Arg8]},_,_,_) ->
1030    [A2,A6,A8] = resolve_args([Arg2,Arg6,Arg8]),
1031    {I,Lbl,A2,W,R,U,A6,decode_field_flags(F),A8};
1032resolve_inst({bs_private_append=I,[Lbl,Arg2,{u,U},Arg4,{u,F},Arg6]},_,_,_) ->
1033    [A2,A4,A6] = resolve_args([Arg2,Arg4,Arg6]),
1034    {I,Lbl,A2,U,A4,decode_field_flags(F),A6};
1035resolve_inst({trim=I,[{u,N},{u,Remaining}]},_,_,_) ->
1036    {I,N,Remaining};
1037resolve_inst({bs_init_bits,[Lbl,Arg2,{u,W},{u,R},{u,F},Arg6]},_,_,_) ->
1038    [A2,A6] = resolve_args([Arg2,Arg6]),
1039    {bs_init_bits,Lbl,A2,W,R,decode_field_flags(F),A6};
1040
1041%%
1042%% R12B-5.
1043%%
1044resolve_inst({bs_get_utf8=I,[Lbl,Arg2,Arg3,{u,U},Arg4]},_,_,_) ->
1045    [A2,A3,A4] = resolve_args([Arg2,Arg3,Arg4]),
1046    {test,I,Lbl,[A2,A3,decode_field_flags(U),A4]};
1047resolve_inst({bs_skip_utf8=I,[Lbl,Arg2,Arg3,{u,U}]},_,_,_) ->
1048    [A2,A3] = resolve_args([Arg2,Arg3]),
1049    {test,I,Lbl,[A2,A3,decode_field_flags(U)]};
1050resolve_inst({bs_get_utf16=I,[Lbl,Arg2,Arg3,{u,U},Arg4]},_,_,_) ->
1051    [A2,A3,A4] = resolve_args([Arg2,Arg3,Arg4]),
1052    {test,I,Lbl,[A2,A3,decode_field_flags(U),A4]};
1053resolve_inst({bs_skip_utf16=I,[Lbl,Arg2,Arg3,{u,U}]},_,_,_) ->
1054    [A2,A3] = resolve_args([Arg2,Arg3]),
1055    {test,I,Lbl,[A2,A3,decode_field_flags(U)]};
1056resolve_inst({bs_get_utf32=I,[Lbl,Arg2,Arg3,{u,U},Arg4]},_,_,_) ->
1057    [A2,A3,A4] = resolve_args([Arg2,Arg3,Arg4]),
1058    {test,I,Lbl,[A2,A3,decode_field_flags(U),A4]};
1059resolve_inst({bs_skip_utf32=I,[Lbl,Arg2,Arg3,{u,U}]},_,_,_) ->
1060    [A2,A3] = resolve_args([Arg2,Arg3]),
1061    {test,I,Lbl,[A2,A3,decode_field_flags(U)]};
1062resolve_inst({bs_utf8_size=I,[Lbl,Arg2,Arg3]},_,_,_) ->
1063    [A2,A3] = resolve_args([Arg2,Arg3]),
1064    {I,Lbl,A2,A3};
1065resolve_inst({bs_put_utf8=I,[Lbl,{u,U},Arg3]},_,_,_) ->
1066    A3 = resolve_arg(Arg3),
1067    {I,Lbl,decode_field_flags(U),A3};
1068resolve_inst({bs_utf16_size=I,[Lbl,Arg2,Arg3]},_,_,_) ->
1069    [A2,A3] = resolve_args([Arg2,Arg3]),
1070    {I,Lbl,A2,A3};
1071resolve_inst({bs_put_utf16=I,[Lbl,{u,U},Arg3]},_,_,_) ->
1072    A3 = resolve_arg(Arg3),
1073    {I,Lbl,decode_field_flags(U),A3};
1074resolve_inst({bs_put_utf32=I,[Lbl,{u,U},Arg3]},_,_,_) ->
1075    A3 = resolve_arg(Arg3),
1076    {I,Lbl,decode_field_flags(U),A3};
1077
1078%%
1079%% R13B03.
1080%%
1081resolve_inst({on_load,[]},_,_,_) ->
1082    on_load;
1083
1084%%
1085%% R14A.
1086%%
1087resolve_inst({recv_mark,[Lbl]},_,_,_) ->
1088    {recv_mark,Lbl};
1089resolve_inst({recv_set,[Lbl]},_,_,_) ->
1090    {recv_set,Lbl};
1091
1092%%
1093%% R15A.
1094%%
1095resolve_inst({line,[Index]},_,_,_) ->
1096    {line,resolve_arg(Index)};
1097
1098%%
1099%% 17.0
1100%%
1101resolve_inst({put_map_assoc,Args},_,_,_) ->
1102    [FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args,
1103    List = resolve_args(List0),
1104    {put_map_assoc,FLbl,Src,Dst,N,{list,List}};
1105resolve_inst({put_map_exact,Args},_,_,_) ->
1106    [FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args,
1107    List = resolve_args(List0),
1108    {put_map_exact,FLbl,Src,Dst,N,{list,List}};
1109resolve_inst({is_map=I,Args0},_,_,_) ->
1110    [FLbl|Args] = resolve_args(Args0),
1111    {test,I,FLbl,Args};
1112resolve_inst({has_map_fields,Args0},_,_,_) ->
1113    [FLbl,Src,{{z,1},{u,_Len},List0}] = Args0,
1114    List = resolve_args(List0),
1115    {test,has_map_fields,FLbl,Src,{list,List}};
1116resolve_inst({get_map_elements,Args0},_,_,_) ->
1117    [FLbl,Src,{{z,1},{u,_Len},List0}] = Args0,
1118    List = resolve_args(List0),
1119    {get_map_elements,FLbl,Src,{list,List}};
1120
1121%%
1122%% OTP 21.
1123%%
1124
1125resolve_inst({build_stacktrace,[]},_,_,_) ->
1126    build_stacktrace;
1127resolve_inst({raw_raise,[]},_,_,_) ->
1128    raw_raise;
1129resolve_inst({get_hd,[Src,Dst]},_,_,_) ->
1130    {get_hd,Src,Dst};
1131resolve_inst({get_tl,[Src,Dst]},_,_,_) ->
1132    {get_tl,Src,Dst};
1133
1134%%
1135%% OTP 22.
1136%%
1137
1138resolve_inst({put_tuple2,[Dst,{{z,1},{u,_},List0}]},_,_,_) ->
1139    List = resolve_args(List0),
1140    {put_tuple2,Dst,{list,List}};
1141resolve_inst({bs_start_match3,[Fail,Bin,Live,Dst]},_,_,_) ->
1142    {bs_start_match3,Fail,Bin,Live,Dst};
1143resolve_inst({bs_get_tail,[Src,Dst,Live]},_,_,_) ->
1144    {bs_get_tail,Src,Dst,Live};
1145resolve_inst({bs_get_position,[Src,Dst,Live]},_,_,_) ->
1146    {bs_get_position,Src,Dst,Live};
1147resolve_inst({bs_set_position,[Src,Dst]},_,_,_) ->
1148    {bs_set_position,Src,Dst};
1149
1150%%
1151%% OTP 23.
1152%%
1153
1154resolve_inst({bs_start_match4,[Fail,Live,Src,Dst]},_,_,_) ->
1155    {bs_start_match4,Fail,Live,Src,Dst};
1156resolve_inst({swap,[_,_]=List},_,_,_) ->
1157    [R1,R2] = resolve_args(List),
1158    {swap,R1,R2};
1159
1160%%
1161%% OTP 24.
1162%%
1163
1164resolve_inst({init_yregs,[{{z,1},{u,_},List0}]},_,_,_) ->
1165    List = resolve_args(List0),
1166    {init_yregs,{list,List}};
1167resolve_inst({recv_marker_bind,[Mark,Ref]},_,_,_) ->
1168    {recv_marker_bind,Mark,Ref};
1169resolve_inst({recv_marker_clear,[Reg]},_,_,_) ->
1170    {recv_marker_clear,Reg};
1171resolve_inst({recv_marker_reserve,[Reg]},_,_,_) ->
1172    {recv_marker_reserve,Reg};
1173resolve_inst({recv_marker_use,[Reg]},_,_,_) ->
1174    {recv_marker_use,Reg};
1175
1176
1177%%
1178%% Catches instructions that are not yet handled.
1179%%
1180resolve_inst(X,_,_,_) -> ?exit({resolve_inst,X}).
1181
1182%%-----------------------------------------------------------------------
1183%% Resolves arguments in a generic way.
1184%%-----------------------------------------------------------------------
1185
1186resolve_args(Args) -> [resolve_arg(A) || A <- Args].
1187
1188resolve_arg({x,N} = Arg) when is_integer(N), N >= 0 -> Arg;
1189resolve_arg({y,N} = Arg) when is_integer(N), N >= 0 -> Arg;
1190resolve_arg({fr,N} = Arg) when is_integer(N), N >= 0 -> Arg;
1191resolve_arg({f,N} = Arg) when is_integer(N), N >= 0 -> Arg;
1192resolve_arg({u,_} = Arg) -> resolve_arg_unsigned(Arg);
1193resolve_arg({i,_} = Arg) -> resolve_arg_integer(Arg);
1194resolve_arg({atom,Atom} = Arg) when is_atom(Atom) -> Arg;
1195resolve_arg({float,F} = Arg) when is_float(F) -> Arg;
1196resolve_arg({literal,_} = Arg) -> Arg;
1197resolve_arg(nil) -> nil.
1198
1199resolve_arg_unsigned({u,N}) when is_integer(N), N >= 0 -> N.
1200
1201resolve_arg_integer({i,N}) when is_integer(N) -> {integer,N}.
1202
1203%%-----------------------------------------------------------------------
1204%% The purpose of the following is just to add a hook for future changes.
1205%% Currently, field flags are numbers 1-2-4-8 and only two of these
1206%% numbers (BSF_LITTLE 2 -- BSF_SIGNED 4) have a semantic significance;
1207%% others are just hints for speeding up the execution; see "erl_bits.h".
1208%%-----------------------------------------------------------------------
1209
1210decode_field_flags(FF) ->
1211    {field_flags,FF}.
1212
1213%%-----------------------------------------------------------------------
1214%% Private Utilities
1215%%-----------------------------------------------------------------------
1216
1217mk_imports(ImportList) ->
1218    gb_trees:from_orddict([{I,{extfunc,M,F,A}} || {I,M,F,A} <- ImportList]).
1219
1220mk_atoms(AtomList) ->
1221    gb_trees:from_orddict(AtomList).
1222
1223mk_labels(LabelList) ->
1224    gb_trees:from_orddict(LabelList).
1225
1226lookup(I, Imports) ->
1227    gb_trees:get(I, Imports).
1228