Module Blambda
blambda is designed to be a lambda-like expression language where every primitive is also a bytecode primitive. This allows us to separate the bytecode backend into two stages:
First, Lambda -> Blambda: Preserves the expression structure, but compiles all complex primitives down to ones with corresponding bytecode instructions. This will become more important as we continue to add more primitives to Lambda which have no corresponding bytecode instruction.
Second, Blambda -> Instructions: Only has to deal with linearizing the Lambda-like control flow. The comparatively fragile stack size maintenance and stack index computations can remain in their own module which doesn't need to be modified every time we change Lambda.
type constant = Lambda.constanttype structured_constant = Lambda.structured_constant = | Const_base of Blambda.constant| Const_block of int * Blambda.structured_constant list| Const_mixed_block of int * Lambda.mixed_block_shape * Blambda.structured_constant list| Const_float_array of string list| Const_immstring of string| Const_float_block of string list| Const_null
structured_constant needs to match the cmo file format
type static_label = Lambda.static_labeltype event = Lambda.lambda_eventtype primitive = | Getglobal of Compilation_unit.t| Getpredef of Ident.t| Boolnot| Isint| Vectlength| Setglobal of Compilation_unit.t| Getfield of int| Getfloatfield of int| Raise of Blambda.raise_kind| Offsetint of int| Offsetref of int| Negint| Addint| Subint| Mulint| Divint| Modint| Andint| Orint| Xorint| Lslint| Lsrint| Asrint| Intcomp of Blambda.comparison| Getstringchar| Getbyteschar| Getvectitem| Setfield of int| Setfloatfield of int| Setvectitem| Setbyteschar| Ccall of string| Makeblock of {}| Makefloatblock| Make_faux_mixedblock of {}| Check_signals
primitives that correspond to bytecode instructions that don't affect control flow
and bfunction = {params : Ident.t list;body : Blambda.blambda;free_variables : Ident.Set.t;(*if we ever intended to do optimizations/transformations on blambda, this would be better as a function than a field
*)
}and blambda = | Var of Ident.t| Const of Blambda.structured_constant| Apply of {func : Blambda.blambda;args : Blambda.blambda list;nontail : bool;
}| Function of Blambda.bfunction| Let of {id : Ident.t;arg : Blambda.blambda;body : Blambda.blambda;
}| Letrec of {decls : Blambda.rec_binding list;free_variables_of_decls : Ident.Set.t;(*if we ever intended to do optimizations/transformations on blambda, this would be better as a function than a field
*)body : Blambda.blambda;
}| Prim of Blambda.primitive * Blambda.blambda list| Switch of {arg : Blambda.blambda;const_cases : int array;(*indexes into
*)cases, indexed by the value of the immediateblock_cases : int array;(*indexes into
*)cases, indexed by the the block tagcases : Blambda.blambda array;
}| Staticraise of Blambda.static_label * Blambda.blambda list| Staticcatch of {id : Blambda.static_label;body : Blambda.blambda;args : Ident.t list;handler : Blambda.blambda;
}| Trywith of {body : Blambda.blambda;param : Ident.t;handler : Blambda.blambda;
}| Sequence of Blambda.blambda * Blambda.blambda| Assign of Ident.t * Blambda.blambda| Send of {method_kind : Blambda.method_kind;met : Blambda.blambda;obj : Blambda.blambda;args : Blambda.blambda list;nontail : bool;
}| Context_switch of Blambda.context_switch * Blambda.blambda list| Ifthenelse of {cond : Blambda.blambda;ifso : Blambda.blambda;ifnot : Blambda.blambda;
}| While of {cond : Blambda.blambda;body : Blambda.blambda;
}| For of {id : Ident.t;from : Blambda.blambda;to_ : Blambda.blambda;dir : Blambda.direction_flag;body : Blambda.blambda;
}| Sequand of Blambda.blambda * Blambda.blambda| Sequor of Blambda.blambda * Blambda.blambda| Event of Blambda.blambda * Lambda.lambda_event| Pseudo_event of Blambda.blambda * Debuginfo.Scoped_location.t(*Pseudo events are ignored by the debugger. They are only used for generating backtraces.
We prefer adding this event here rather than in lambda generation because:
- There are many different situations where a Pmakeblock can be generated.
- We prefer inserting a pseudo event rather than an event after to prevent the debugger to stop at every single allocation.
Having
Eventand/orPseudo_eventmake effective pattern-matching on blambda hard. However, blambda is only meant to go immediately before the code generator, so it shouldn't really be matched on anyway.In the future, we could simplify things a bit and use a new
*)Lev_pseudo_afterevent kind in theEventconstructor instead of Pseudo_event, to generate during lambda to blambda conversion if!Clflags.debugistrue.