1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2020. 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%% Purpose : Assembler for threaded Beam.
21
22-module(beam_asm).
23
24-export([module/4]).
25-export([encode/2]).
26
27-export_type([fail/0,label/0,reg/0,reg_num/0,src/0,module_code/0,function_name/0]).
28
29-import(lists, [map/2,member/2,keymember/3,duplicate/2,splitwith/2]).
30-include("beam_opcodes.hrl").
31
32%% Common types for describing operands for BEAM instructions.
33-type reg_num() :: 0..1023.
34-type reg() :: {'x',reg_num()} | {'y',reg_num()}.
35-type src() :: reg() |
36	       {'literal',term()} |
37	       {'atom',atom()} |
38	       {'integer',integer()} |
39	       'nil' |
40	       {'float',float()}.
41-type label() :: pos_integer().
42-type fail() :: {'f',label() | 0}.
43
44%% asm_instruction() describes only the instructions that
45%% are used in BEAM files (as opposed to internal instructions
46%% used only during optimization).
47
48-type asm_instruction() :: atom() | tuple().
49
50-type function_name() :: atom().
51
52-type asm_function() ::
53        {'function',function_name(),arity(),label(),[asm_instruction()]}.
54
55-type module_code() ::
56        {module(),[_],[_],[asm_function()],pos_integer()}.
57
58-spec module(module_code(), [{binary(), binary()}], [{atom(),term()}], [compile:option()]) ->
59                    {'ok',binary()}.
60
61module(Code, ExtraChunks, CompileInfo, CompilerOpts) ->
62    {ok,assemble(Code, ExtraChunks, CompileInfo, CompilerOpts)}.
63
64assemble({Mod,Exp0,Attr0,Asm0,NumLabels}, ExtraChunks, CompileInfo, CompilerOpts) ->
65    {1,Dict0} = beam_dict:atom(Mod, beam_dict:new()),
66    {0,Dict1} = beam_dict:fname(atom_to_list(Mod) ++ ".erl", Dict0),
67    Dict2 = shared_fun_wrappers(CompilerOpts, Dict1),
68    NumFuncs = length(Asm0),
69    {Asm,Attr} = on_load(Asm0, Attr0),
70    Exp = cerl_sets:from_list(Exp0),
71    {Code,Dict} = assemble_1(Asm, Exp, Dict2, []),
72    build_file(Code, Attr, Dict, NumLabels, NumFuncs,
73               ExtraChunks, CompileInfo, CompilerOpts).
74
75shared_fun_wrappers(Opts, Dict) ->
76    case proplists:get_bool(no_shared_fun_wrappers, Opts) of
77        false ->
78            %% The compiler in OTP 23 depends on the on the loader
79            %% using the new indices in funs and being able to have
80            %% multiple make_fun2 instructions referring to the same
81            %% fun entry. Artificially set the highest opcode for the
82            %% module to ensure that it cannot be loaded in OTP 22
83            %% and earlier.
84            Swap = beam_opcodes:opcode(swap, 2),
85            beam_dict:opcode(Swap, Dict);
86        true ->
87            %% Fun wrappers are not shared for compatibility with a
88            %% previous OTP release.
89            Dict
90    end.
91
92on_load(Fs0, Attr0) ->
93    case proplists:get_value(on_load, Attr0) of
94	undefined ->
95	    {Fs0,Attr0};
96	[{Name,0}] ->
97	    Fs = map(fun({function,N,0,Entry,Is0}) when N =:= Name ->
98			     Is = insert_on_load_instruction(Is0, Entry),
99			     {function,N,0,Entry,Is};
100			(F) ->
101			     F
102		     end, Fs0),
103	    Attr = proplists:delete(on_load, Attr0),
104	    {Fs,Attr}
105    end.
106
107insert_on_load_instruction(Is0, Entry) ->
108    {Bef,[{label,Entry}=El|Is]} =
109	splitwith(fun({label,L}) when L =:= Entry -> false;
110		     (_) -> true
111		  end, Is0),
112    Bef ++ [El,on_load|Is].
113
114assemble_1([{function,Name,Arity,Entry,Asm}|T], Exp, Dict0, Acc) ->
115    Dict1 = case cerl_sets:is_element({Name,Arity}, Exp) of
116		true ->
117		    beam_dict:export(Name, Arity, Entry, Dict0);
118		false ->
119		    beam_dict:local(Name, Arity, Entry, Dict0)
120	    end,
121    {Code, Dict2} = assemble_function(Asm, Acc, Dict1),
122    assemble_1(T, Exp, Dict2, Code);
123assemble_1([], _Exp, Dict0, Acc) ->
124    {IntCodeEnd,Dict1} = make_op(int_code_end, Dict0),
125    {list_to_binary(lists:reverse(Acc, [IntCodeEnd])),Dict1}.
126
127assemble_function([H|T], Acc, Dict0) ->
128    {Code, Dict} = make_op(H, Dict0),
129    assemble_function(T, [Code| Acc], Dict);
130assemble_function([], Code, Dict) ->
131    {Code, Dict}.
132
133build_file(Code, Attr, Dict, NumLabels, NumFuncs, ExtraChunks, CompileInfo, CompilerOpts) ->
134    %% Create the code chunk.
135
136    CodeChunk = chunk(<<"Code">>,
137		      <<16:32,
138		       (beam_opcodes:format_number()):32,
139		       (beam_dict:highest_opcode(Dict)):32,
140		       NumLabels:32,
141		       NumFuncs:32>>,
142		      Code),
143
144    %% Create the atom table chunk.
145    AtomEncoding = atom_encoding(CompilerOpts),
146    {NumAtoms, AtomTab} = beam_dict:atom_table(Dict, AtomEncoding),
147    AtomChunk = chunk(atom_chunk_name(AtomEncoding), <<NumAtoms:32>>, AtomTab),
148
149    %% Create the import table chunk.
150
151    {NumImps, ImpTab0} = beam_dict:import_table(Dict),
152    Imp = flatten_imports(ImpTab0),
153    ImportChunk = chunk(<<"ImpT">>, <<NumImps:32>>, Imp),
154
155    %% Create the export table chunk.
156
157    {NumExps, ExpTab0} = beam_dict:export_table(Dict),
158    Exp = flatten_exports(ExpTab0),
159    ExpChunk = chunk(<<"ExpT">>, <<NumExps:32>>, Exp),
160
161    %% Create the local function table chunk.
162
163    {NumLocals, Locals} = beam_dict:local_table(Dict),
164    Loc = flatten_exports(Locals),
165    LocChunk = chunk(<<"LocT">>, <<NumLocals:32>>, Loc),
166
167    %% Create the string table chunk.
168
169    {_,StringTab} = beam_dict:string_table(Dict),
170    StringChunk = chunk(<<"StrT">>, StringTab),
171
172    %% Create the fun table chunk. It is important not to build an empty chunk,
173    %% as that would change the MD5.
174
175    LambdaChunk = case beam_dict:lambda_table(Dict) of
176		      {0,[]} -> [];
177		      {NumLambdas,LambdaTab} ->
178			  chunk(<<"FunT">>, <<NumLambdas:32>>, LambdaTab)
179		  end,
180
181    %% Create the literal table chunk. It is important not to build an empty chunk,
182    %% as that would change the MD5.
183
184    LiteralChunk = case beam_dict:literal_table(Dict) of
185		       {0,[]} -> [];
186		       {NumLiterals,LitTab0} ->
187			   LitTab1 = [<<NumLiterals:32>>,LitTab0],
188			   LitTab = zlib:compress(LitTab1),
189			   chunk(<<"LitT">>, <<(iolist_size(LitTab1)):32>>,
190				 LitTab)
191		   end,
192
193    %% Create the line chunk.
194
195    LineChunk = chunk(<<"Line">>, build_line_table(Dict)),
196
197    %% Create the attributes and compile info chunks.
198
199    Essentials0 = [AtomChunk,CodeChunk,StringChunk,ImportChunk,
200		   ExpChunk,LambdaChunk,LiteralChunk],
201    Essentials1 = [iolist_to_binary(C) || C <- Essentials0],
202    MD5 = module_md5(Essentials1),
203    Essentials = finalize_fun_table(Essentials1, MD5),
204    {Attributes,Compile} = build_attributes(Attr, CompileInfo, MD5),
205    AttrChunk = chunk(<<"Attr">>, Attributes),
206    CompileChunk = chunk(<<"CInf">>, Compile),
207
208    %% Compile all extra chunks.
209
210    CheckedChunks = [chunk(Key, Value) || {Key, Value} <- ExtraChunks],
211
212    %% Create IFF chunk.
213
214    Chunks = case member(slim, CompilerOpts) of
215		 true ->
216		     [Essentials,AttrChunk];
217		 false ->
218		     [Essentials,LocChunk,AttrChunk,
219		      CompileChunk,CheckedChunks,LineChunk]
220	     end,
221    build_form(<<"BEAM">>, Chunks).
222
223atom_encoding(Opts) ->
224    case proplists:get_bool(no_utf8_atoms, Opts) of
225	false -> utf8;
226	true -> latin1
227    end.
228
229atom_chunk_name(utf8) -> <<"AtU8">>;
230atom_chunk_name(latin1) -> <<"Atom">>.
231
232%% finalize_fun_table(Essentials, MD5) -> FinalizedEssentials
233%%  Update the 'old_uniq' field in the entry for each fun in the
234%%  'FunT' chunk. We'll use part of the MD5 for the module as a
235%%  unique value.
236
237finalize_fun_table(Essentials, MD5) ->
238    [finalize_fun_table_1(E, MD5) || E <- Essentials].
239
240finalize_fun_table_1(<<"FunT",Keep:8/binary,Table0/binary>>, MD5) ->
241    <<Uniq:27,_:101/bits>> = MD5,
242    Table = finalize_fun_table_2(Table0, Uniq, <<>>),
243    <<"FunT",Keep/binary,Table/binary>>;
244finalize_fun_table_1(Chunk, _) -> Chunk.
245
246finalize_fun_table_2(<<Keep:20/binary,0:32,T/binary>>, Uniq, Acc) ->
247    finalize_fun_table_2(T, Uniq, <<Acc/binary,Keep/binary,Uniq:32>>);
248finalize_fun_table_2(<<>>, _, Acc) -> Acc.
249
250%% Build an IFF form.
251
252build_form(Id, Chunks0) when byte_size(Id) =:= 4, is_list(Chunks0) ->
253    Chunks = list_to_binary(Chunks0),
254    Size = byte_size(Chunks),
255    0 = Size rem 4,				% Assertion: correct padding?
256    <<"FOR1",(Size+4):32,Id/binary,Chunks/binary>>.
257
258%% Build a correctly padded chunk (with no sub-header).
259
260chunk(Id, Contents) when byte_size(Id) =:= 4, is_binary(Contents) ->
261    Size = byte_size(Contents),
262    [<<Id/binary,Size:32>>,Contents|pad(Size)].
263
264%% Build a correctly padded chunk (with a sub-header).
265
266chunk(Id, Head, Contents) when byte_size(Id) =:= 4, is_binary(Head), is_binary(Contents) ->
267    Size = byte_size(Head)+byte_size(Contents),
268    [<<Id/binary,Size:32,Head/binary>>,Contents|pad(Size)];
269chunk(Id, Head, Contents) when is_list(Contents) ->
270    chunk(Id, Head, list_to_binary(Contents)).
271
272pad(Size) ->
273    case Size rem 4 of
274	0 -> [];
275	Rem -> duplicate(4 - Rem, 0)
276    end.
277
278flatten_exports(Exps) ->
279    list_to_binary(map(fun({F,A,L}) -> <<F:32,A:32,L:32>> end, Exps)).
280
281flatten_imports(Imps) ->
282    list_to_binary(map(fun({M,F,A}) -> <<M:32,F:32,A:32>> end, Imps)).
283
284build_attributes(Attr, Compile, MD5) ->
285    AttrBinary = term_to_binary(set_vsn_attribute(Attr, MD5)),
286    CompileBinary = term_to_binary([{version,?COMPILER_VSN}|Compile]),
287    {AttrBinary,CompileBinary}.
288
289build_line_table(Dict) ->
290    {NumLineInstrs,NumFnames0,Fnames0,NumLines,Lines0} =
291	beam_dict:line_table(Dict),
292    NumFnames = NumFnames0 - 1,
293    [_|Fnames1] = Fnames0,
294    Fnames2 = [unicode:characters_to_binary(F) || F <- Fnames1],
295    Fnames = << <<(byte_size(F)):16,F/binary>> || F <- Fnames2 >>,
296    Lines1 = encode_line_items(Lines0, 0),
297    Lines = iolist_to_binary(Lines1),
298    Ver = 0,
299    Bits = 0,
300    <<Ver:32,Bits:32,NumLineInstrs:32,NumLines:32,NumFnames:32,
301     Lines/binary,Fnames/binary>>.
302
303%% encode_line_items([{FnameIndex,Line}], PrevFnameIndex)
304%%  Encode the line items compactly. Tag the FnameIndex with
305%%  an 'a' tag (atom) and only include it when it has changed.
306%%  Tag the line numbers with an 'i' (integer) tag.
307
308encode_line_items([{F,L}|T], F) ->
309    [encode(?tag_i, L)|encode_line_items(T, F)];
310encode_line_items([{F,L}|T], _) ->
311    [encode(?tag_a, F),encode(?tag_i, L)|encode_line_items(T, F)];
312encode_line_items([], _) -> [].
313
314%%
315%% If the attributes contains no 'vsn' attribute, we'll insert one
316%% with an MD5 "checksum" calculated on the code as its value.
317%% We'll not change an existing 'vsn' attribute.
318%%
319
320set_vsn_attribute(Attr, MD5) ->
321    case keymember(vsn, 1, Attr) of
322	true -> Attr;
323	false ->
324	    <<Number:128>> = MD5,
325	    [{vsn,[Number]}|Attr]
326    end.
327
328module_md5(Essentials0) ->
329    Essentials = filter_essentials(Essentials0),
330    erlang:md5(Essentials).
331
332%% filter_essentials([Chunk]) -> [Chunk']
333%%  Filter essentials so that we obtain the same MD5 as code:module_md5/1 and
334%%  beam_lib:md5/1 would calculate for this module.  Note that at this
335%%  point, the 'old_uniq' entry for each fun in the 'FunT' chunk is zeroed,
336%%  so there is no need to go through the 'FunT' chunk.
337
338filter_essentials([<<_Tag:4/binary,Sz:32,Data:Sz/binary,_Padding/binary>>|T]) ->
339    [Data|filter_essentials(T)];
340filter_essentials([<<>>|T]) ->
341    filter_essentials(T);
342filter_essentials([]) -> [].
343
344bif_type(fnegate, 1) -> {op,fnegate};
345bif_type(fadd, 2)   -> {op,fadd};
346bif_type(fsub, 2)   -> {op,fsub};
347bif_type(fmul, 2)   -> {op,fmul};
348bif_type(fdiv, 2)   -> {op,fdiv};
349bif_type(_, 1)      -> bif1;
350bif_type(_, 2)      -> bif2.
351
352make_op({'%',_}, Dict) ->
353    {[],Dict};
354make_op({line,Location}, Dict0) ->
355    {Index,Dict} = beam_dict:line(Location, Dict0),
356    encode_op(line, [Index], Dict);
357make_op({bif, Bif, {f,_}, [], Dest}, Dict) ->
358    %% BIFs without arguments cannot fail.
359    encode_op(bif0, [{extfunc, erlang, Bif, 0}, Dest], Dict);
360make_op({bif, raise, _Fail, [_A1,_A2] = Args, _Dest}, Dict) ->
361    encode_op(raise, Args, Dict);
362make_op({bif,Bif,Fail,Args,Dest}, Dict) ->
363    Arity = length(Args),
364    case bif_type(Bif, Arity) of
365	{op,Op} ->
366	    make_op(list_to_tuple([Op,Fail|Args++[Dest]]), Dict);
367	BifOp when is_atom(BifOp) ->
368	    encode_op(BifOp, [Fail,{extfunc,erlang,Bif,Arity}|Args++[Dest]],
369		      Dict)
370    end;
371make_op({gc_bif,Bif,Fail,Live,Args,Dest}, Dict) ->
372    Arity = length(Args),
373    BifOp = case Arity of
374		1 -> gc_bif1;
375		2 -> gc_bif2;
376		3 -> gc_bif3
377	    end,
378    encode_op(BifOp, [Fail,Live,{extfunc,erlang,Bif,Arity}|Args++[Dest]],Dict);
379make_op({bs_add=Op,Fail,[Src1,Src2,Unit],Dest}, Dict) ->
380    encode_op(Op, [Fail,Src1,Src2,Unit,Dest], Dict);
381make_op({test,Cond,Fail,Src,{list,_}=Ops}, Dict) ->
382    encode_op(Cond, [Fail,Src,Ops], Dict);
383make_op({test,Cond,Fail,Ops}, Dict) when is_list(Ops) ->
384    encode_op(Cond, [Fail|Ops], Dict);
385make_op({test,Cond,Fail,Live,[Op|Ops],Dst}, Dict) when is_list(Ops) ->
386    encode_op(Cond, [Fail,Op,Live|Ops++[Dst]], Dict);
387make_op({make_fun2,{f,Lbl},_Index,_OldUniq,NumFree}, Dict0) ->
388    {Fun,Dict} = beam_dict:lambda(Lbl, NumFree, Dict0),
389    make_op({make_fun2,Fun}, Dict);
390make_op({kill,Y}, Dict) ->
391    make_op({init,Y}, Dict);
392make_op({Name,Arg1}, Dict) ->
393    encode_op(Name, [Arg1], Dict);
394make_op({Name,Arg1,Arg2}, Dict) ->
395    encode_op(Name, [Arg1,Arg2], Dict);
396make_op({Name,Arg1,Arg2,Arg3}, Dict) ->
397    encode_op(Name, [Arg1,Arg2,Arg3], Dict);
398make_op({Name,Arg1,Arg2,Arg3,Arg4}, Dict) ->
399    encode_op(Name, [Arg1,Arg2,Arg3,Arg4], Dict);
400make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5}, Dict) ->
401    encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5], Dict);
402make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6}, Dict) ->
403    encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6], Dict);
404%% make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7}, Dict) ->
405%%     encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7], Dict);
406make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8}, Dict) ->
407    encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8], Dict);
408make_op(Op, Dict) when is_atom(Op) ->
409    encode_op(Op, [], Dict).
410
411encode_op(Name, Args, Dict0) when is_atom(Name) ->
412    Op = beam_opcodes:opcode(Name, length(Args)),
413    Dict = beam_dict:opcode(Op, Dict0),
414    encode_op_1(Args, Dict, Op).
415
416encode_op_1([A0|As], Dict0, Acc) ->
417    {A,Dict} = encode_arg(A0, Dict0),
418    encode_op_1(As, Dict, [Acc,A]);
419encode_op_1([], Dict, Acc) -> {Acc,Dict}.
420
421encode_arg({x, X}, Dict) when is_integer(X), X >= 0 ->
422    {encode(?tag_x, X), Dict};
423encode_arg({y, Y}, Dict) when is_integer(Y), Y >= 0 ->
424    {encode(?tag_y, Y), Dict};
425encode_arg({atom, Atom}, Dict0) when is_atom(Atom) ->
426    {Index, Dict} = beam_dict:atom(Atom, Dict0),
427    {encode(?tag_a, Index), Dict};
428encode_arg({integer, N}, Dict) ->
429    %% Conservatively assume that all integers whose absolute
430    %% value is greater than 1 bsl 128 will be bignums in
431    %% the runtime system.
432    if
433        N >= 1 bsl 128 ->
434            encode_literal(N, Dict);
435        N =< -(1 bsl 128) ->
436            encode_literal(N, Dict);
437        true ->
438            {encode(?tag_i, N), Dict}
439    end;
440encode_arg(nil, Dict) ->
441    {encode(?tag_a, 0), Dict};
442encode_arg({f, W}, Dict) ->
443    {encode(?tag_f, W), Dict};
444%% encode_arg({'char', C}, Dict) ->
445%%     {encode(?tag_h, C), Dict};
446encode_arg({string, BinString}, Dict0) when is_binary(BinString) ->
447    {Offset, Dict} = beam_dict:string(BinString, Dict0),
448    {encode(?tag_u, Offset), Dict};
449encode_arg({extfunc, M, F, A}, Dict0) ->
450    {Index, Dict} = beam_dict:import(M, F, A, Dict0),
451    {encode(?tag_u, Index), Dict};
452encode_arg({list, List}, Dict0) ->
453    {L, Dict} = encode_list(List, Dict0, []),
454    {[encode(?tag_z, 1), encode(?tag_u, length(List))|L], Dict};
455encode_arg({float, Float}, Dict) when is_float(Float) ->
456    encode_literal(Float, Dict);
457encode_arg({fr,Fr}, Dict) ->
458    {[encode(?tag_z, 2),encode(?tag_u, Fr)], Dict};
459encode_arg({field_flags,Flags0}, Dict) ->
460    Flags = lists:foldl(fun (F, S) -> S bor flag_to_bit(F) end, 0, Flags0),
461    {encode(?tag_u, Flags), Dict};
462encode_arg({alloc,List}, Dict) ->
463    encode_alloc_list(List, Dict);
464encode_arg({literal,Lit}, Dict) ->
465    if
466        Lit =:= [] ->
467            encode_arg(nil, Dict);
468        is_atom(Lit) ->
469            encode_arg({atom,Lit}, Dict);
470        is_integer(Lit) ->
471            encode_arg({integer,Lit}, Dict);
472        true ->
473            encode_literal(Lit, Dict)
474    end;
475encode_arg(Int, Dict) when is_integer(Int) ->
476    {encode(?tag_u, Int),Dict}.
477
478encode_literal(Literal, Dict0) ->
479    {Index,Dict} = beam_dict:literal(Literal, Dict0),
480    {[encode(?tag_z, 4),encode(?tag_u, Index)],Dict}.
481
482%%flag_to_bit(aligned) -> 16#01; %% No longer useful.
483flag_to_bit(little)  -> 16#02;
484flag_to_bit(big)     -> 16#00;
485flag_to_bit(signed)  -> 16#04;
486flag_to_bit(unsigned)-> 16#00;
487%%flag_to_bit(exact)   -> 16#08;
488flag_to_bit(native)  -> 16#10;
489flag_to_bit({anno,_}) -> 0.
490
491encode_list([H|T], Dict0, Acc) when not is_list(H) ->
492    {Enc,Dict} = encode_arg(H, Dict0),
493    encode_list(T, Dict, [Acc,Enc]);
494encode_list([], Dict, Acc) -> {Acc,Dict}.
495
496encode_alloc_list(L0, Dict0) ->
497    {Bin,Dict} = encode_alloc_list_1(L0, Dict0, []),
498    {[encode(?tag_z, 3),encode(?tag_u, length(L0)),Bin],Dict}.
499
500encode_alloc_list_1([{words,Words}|T], Dict, Acc0) ->
501    Acc = [Acc0,encode(?tag_u, 0),encode(?tag_u, Words)],
502    encode_alloc_list_1(T, Dict, Acc);
503encode_alloc_list_1([{floats,Floats}|T], Dict, Acc0) ->
504    Acc = [Acc0,encode(?tag_u, 1),encode(?tag_u, Floats)],
505    encode_alloc_list_1(T, Dict, Acc);
506encode_alloc_list_1([], Dict, Acc) ->
507    {iolist_to_binary(Acc),Dict}.
508
509-spec encode(non_neg_integer(), integer()) -> iolist() | integer().
510
511encode(Tag, N) when N < 0 ->
512    encode1(Tag, negative_to_bytes(N));
513encode(Tag, N) when N < 16 ->
514    (N bsl 4) bor Tag;
515encode(Tag, N) when N < 16#800  ->
516    [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
517encode(Tag, N) ->
518    encode1(Tag, to_bytes(N)).
519
520encode1(Tag, Bytes) ->
521    case iolist_size(Bytes) of
522	Num when 2 =< Num, Num =< 8 ->
523	    [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
524	Num when 8 < Num ->
525	    [2#11111000 bor Tag, encode(?tag_u, Num-9)| Bytes]
526    end.
527
528to_bytes(N) ->
529    Bin = binary:encode_unsigned(N),
530    case Bin of
531	<<0:1,_/bits>> -> Bin;
532	<<1:1,_/bits>> -> [0,Bin]
533    end.
534
535negative_to_bytes(N) when N >= -16#8000 ->
536    <<N:16>>;
537negative_to_bytes(N) ->
538    Bytes = byte_size(binary:encode_unsigned(-N)),
539    Bin = <<N:Bytes/unit:8>>,
540    case Bin of
541	<<0:1,_/bits>> -> [16#ff,Bin];
542	<<1:1,_/bits>> -> Bin
543    end.
544