File 2641-Refactor-the-transformation-engine.patch of Package erlang

From fb65455c92f8f8e9bcc8d6f7b8b7ca9738ff5c7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Mon, 1 Jun 2020 08:27:09 +0200
Subject: [PATCH] Refactor the transformation engine

The predicates (guard constraints) and generators for transformation
rules were static functions the `beam_load.c` source code files.
Break out the predicates and generators to `predicates.tab` and
`generators.tab`, respectively.
---
 erts/emulator/Makefile.in                   |   12 +-
 erts/emulator/beam/beam_load.c              | 2852 +------------------
 erts/emulator/beam/beam_load.h              |  313 ++
 erts/emulator/beam/beam_transform_engine.c  |  427 +++
 erts/emulator/beam/beam_transform_helpers.c |  162 ++
 erts/emulator/beam/beam_transform_helpers.h |   28 +
 erts/emulator/beam/float_instrs.tab         |   10 -
 erts/emulator/beam/generators.tab           | 1270 +++++++++
 erts/emulator/beam/ops.tab                  |   89 +-
 erts/emulator/beam/predicates.tab           |  269 ++
 erts/emulator/internal_doc/beam_makeops.md  |  103 +-
 erts/emulator/utils/beam_makeops            |  118 +-
 12 files changed, 2779 insertions(+), 2874 deletions(-)
 create mode 100644 erts/emulator/beam/beam_transform_engine.c
 create mode 100644 erts/emulator/beam/beam_transform_helpers.c
 create mode 100644 erts/emulator/beam/beam_transform_helpers.h
 create mode 100644 erts/emulator/beam/generators.tab
 create mode 100644 erts/emulator/beam/predicates.tab

diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 6760207fa8..6c25a28116 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -29,13 +29,14 @@ HIPE_ENABLED=@HIPE_ENABLED@
 DTRACE_ENABLED=@DTRACE_ENABLED@
 DTRACE_ENABLED_2STEP=@DTRACE_ENABLED_2STEP@
 USE_VM_PROBES=@USE_VM_PROBES@
-FPE=@FPE@
 LIBS = @LIBS@
 Z_LIB=@Z_LIB@
 CROSS_COMPILING = @CROSS_COMPILING@
 NO_INLINE_FUNCTIONS=false
 OPCODE_TABLES = $(ERL_TOP)/lib/compiler/src/genop.tab \
 beam/ops.tab \
+beam/predicates.tab \
+beam/generators.tab \
 beam/macros.tab \
 beam/instrs.tab \
 beam/arith_instrs.tab \
@@ -45,7 +46,7 @@ beam/float_instrs.tab \
 beam/map_instrs.tab \
 beam/msg_instrs.tab \
 beam/select_instrs.tab \
-beam/trace_instrs.tab
+beam/trace_instrs.tab \
 
 DEBUG_CFLAGS = @DEBUG_CFLAGS@
 CONFIGURE_CFLAGS = @CFLAGS@
@@ -562,8 +563,7 @@ $(TTF_DIR)/beam_warm.h \
 $(TTF_DIR)/beam_hot.h \
 $(TTF_DIR)/beam_opcodes.c \
 $(TTF_DIR)/beam_opcodes.h \
-$(TTF_DIR)/beam_pred_funcs.h \
-$(TTF_DIR)/beam_tr_funcs.h \
+$(TTF_DIR)/beam_transform.c \
 	: $(TTF_DIR)/OPCODES-GENERATED
 $(TTF_DIR)/OPCODES-GENERATED: $(OPCODE_TABLES) utils/beam_makeops
 	$(gen_verbose)LANG=C $(PERL) utils/beam_makeops \
@@ -571,7 +571,6 @@ $(TTF_DIR)/OPCODES-GENERATED: $(OPCODE_TABLES) utils/beam_makeops
 		-code-model @CODE_MODEL@ \
 		-outdir $(TTF_DIR) \
 		-DUSE_VM_PROBES=$(if $(USE_VM_PROBES),1,0) \
-		-DNO_FPE_SIGNALS=$(if $(filter unreliable,$(FPE)),1,0) \
 		-emulator $(OPCODE_TABLES) && echo $? >$(TTF_DIR)/OPCODES-GENERATED
 GENERATE += $(TTF_DIR)/OPCODES-GENERATED
 
@@ -864,6 +863,9 @@ PROFILE_OBJS = $(OBJDIR)/beam_emu.o	$(OBJDIR)/erl_process.o
 
 EMU_OBJS = \
 	$(OBJDIR)/beam_opcodes.o \
+	$(OBJDIR)/beam_transform.o \
+	$(OBJDIR)/beam_transform_engine.o \
+	$(OBJDIR)/beam_transform_helpers.o \
 	$(OBJDIR)/beam_load.o		$(OBJDIR)/beam_bif_load.o \
 	$(OBJDIR)/beam_debug.o		$(OBJDIR)/beam_bp.o \
 	$(OBJDIR)/beam_catches.o 	$(OBJDIR)/code_ix.o \
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 7d81ef268a..b5cf78c35d 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -51,111 +51,10 @@
 
 ErlDrvBinary* erts_gzinflate_buffer(char*, int);
 
-#define MAX_OPARGS 8
 #define CALLED    0
 #define DEFINED   1
 #define EXPORTED  2
 
-#if defined(WORDS_BIGENDIAN)
-# define NATIVE_ENDIAN(F)			\
-  if ((F).val & BSF_NATIVE) {			\
-     (F).val &= ~(BSF_LITTLE|BSF_NATIVE);	\
-  } else {}
-#else
-# define NATIVE_ENDIAN(F)			\
-  if ((F).val & BSF_NATIVE) {			\
-      (F).val &= ~BSF_NATIVE;			\
-      (F).val |= BSF_LITTLE;			\
-  } else {}
-#endif
-
-/*
- * Errors returned from tranform_engine().
- */
-#define TE_OK 0
-#define TE_FAIL (-1)
-#define TE_SHORT_WINDOW (-2)
-
-/*
- * Type for a reference to a label that must be patched.
- */
-
-typedef struct {
-    Uint pos;                   /* Position of label reference to patch. */
-    Uint offset;                /* Offset from patch location.  */
-    int packed;                 /* 0 (not packed), 1 (lsw), 2 (msw) */
-} LabelPatch;
-
-/*
- * Type for a label.
- */
-
-typedef struct {
-    Uint value;			/* Value of label (0 if not known yet). */
-    Uint looprec_targeted;	/* Non-zero if this label is the target of a loop_rec
-				 * instruction.
-				 */
-    LabelPatch* patches;       /* Array of label patches. */
-    Uint num_patches;           /* Number of patches in array. */
-    Uint num_allocated;         /* Number of allocated patches. */
-} Label;
-
-/*
- * Type for an operand for a generic instruction.
- */
-
-typedef struct {
-    unsigned type;		/* Type of operand. */
-    BeamInstr val;		/* Value of operand. */
-} GenOpArg;
-
-/*
- * A generic operation.
- */
-
-typedef struct genop {
-    unsigned int op;		/* Opcode. */
-    int arity;			/* Number of arguments. */
-    GenOpArg def_args[MAX_OPARGS]; /* Default buffer for arguments. */
-    GenOpArg* a;		/* The arguments. */
-    struct genop* next;		/* Next genop. */
-} GenOp;
-
-/*
- * The allocation unit for generic blocks.
- */
-  
-typedef struct genop_block {
-    GenOp genop[32];
-    struct genop_block* next;
-} GenOpBlock;
-
-/*
- * This structure contains information for an imported function or BIF.
- */
-typedef struct {
-    Eterm module;		/* Tagged atom for module. */
-    Eterm function;		/* Tagged atom for function. */
-    int arity;			/* Arity. */
-    Uint patches;		/* Index to locations in code to
-				 * eventually patch with a pointer into
-				 * the export entry.
-				 */
-    Export *bif;		/* Pointer to export entry if BIF;
-				 * NULL otherwise.
-				 */
-} ImportEntry;
-
-/*
- * This structure contains information for a function exported from a module.
- */
-
-typedef struct {
-    Eterm function;		/* Tagged atom for function. */
-    int arity;			/* Arity. */
-    BeamInstr* address;		/* Address to function in code. */
-} ExportEntry;
-
 #define MakeIffId(a, b, c, d) \
   (((Uint) (a) << 24) | ((Uint) (b) << 16) | ((Uint) (c) << 8) | (Uint) (d))
 
@@ -174,13 +73,11 @@ typedef struct {
 #define LINE_CHUNK 9
 #define UTF8_ATOM_CHUNK 10
 
-#define NUM_CHUNK_TYPES (sizeof(chunk_types)/sizeof(chunk_types[0]))
-
 /*
  * An array with all chunk types recognized by the loader.
  */
 
-static Uint chunk_types[] = {
+static Uint chunk_types[ERTS_BEAM_NUM_CHUNK_TYPES] = {
     /*
      * Atom chunk types -- Atom or AtU8 MUST be present.
      */
@@ -205,172 +102,6 @@ static Uint chunk_types[] = {
     MakeIffId('A', 't', 'U', '8'), /* 10 */
 };
 
-/*
- * This structure keeps load-time information about a lambda.
- */
-
-typedef struct {
-    ErlFunEntry* fe;		/* Entry in fun table. */
-    unsigned label;		/* Label of function entry. */
-    Uint32 num_free;		/* Number of free variables. */
-    Eterm function;		/* Name of local function. */
-    int arity;			/* Arity (including free variables). */
-} Lambda;
-
-/*
- * This structure keeps load-time information about a literal.
- */
-
-typedef struct {
-    Eterm term;			/* The tagged term (in the heap). */
-    ErlHeapFragment* heap_frags;
-} Literal;
-
-/*
- * This structure keeps information about an operand that needs to be
- * patched to contain the correct address of a literal when the code is
- * frozen.
- */
-
-typedef struct literal_patch LiteralPatch;
-struct literal_patch {
-    Uint pos;			/* Position in code */
-    LiteralPatch* next;
-};
-
-/*
- * This structure keeps information about an operand that needs to be
- * patched to contain the correct address for an address into the string table.
- */
-
-typedef struct string_patch StringPatch;
-struct string_patch {
-    int pos;			/* Position in code */
-    StringPatch* next;
-};
-
-/*
- * This structure associates a code offset with a source code location.
- */
-
-typedef struct {
-    int pos;			/* Position in code */
-    Uint32 loc;			/* Location in source code */
-} LineInstr;
-
-/*
- * This structure contains all information about the module being loaded.
- */  
-#define MD5_SIZE 16
-typedef struct LoaderState {
-    /*
-     * The current logical file within the binary.
-     */
-
-    char* file_name;		/* Name of file we are reading (usually chunk name). */
-    byte* file_p;		/* Current pointer within file. */
-    unsigned file_left;		/* Number of bytes left in file. */
-    ErlDrvBinary* bin;		/* Binary holding BEAM file (or NULL) */
-
-    /*
-     * The following are used mainly for diagnostics.
-     */
-
-    Eterm group_leader;		/* Group leader (for diagnostics). */
-    Eterm module;		/* Tagged atom for module name. */
-    Eterm function;		/* Tagged atom for current function
-				 * (or 0 if none).
-				 */
-    unsigned arity;		/* Arity for current function. */
-
-    /*
-     * All found chunks.
-     */
-
-    struct {
-	byte* start;		/* Start of chunk (in binary). */
-	unsigned size;		/* Size of chunk. */
-    } chunks[NUM_CHUNK_TYPES];
-
-    /*
-     * Used for code loading (mainly).
-     */
-
-    byte* code_start;		/* Start of code file. */
-    unsigned code_size;		/* Size of code file. */
-    int specific_op;		/* Specific opcode (-1 if not found). */
-    unsigned int num_functions; /* Number of functions in module. */
-    unsigned int num_labels;	/* Number of labels. */
-    BeamCodeHeader* hdr;	/* Loaded code header */
-    BeamInstr* codev;	        /* Loaded code buffer */
-    int        codev_size;      /* Size of code buffer in words. */
-    int ci;			/* Current index into loaded code buffer. */
-    Label* labels;
-    StringPatch* string_patches; /* Linked list of position into string table to patch. */
-    BeamInstr catches;		/* Linked list of catch_yf instructions. */
-    unsigned loaded_size;	/* Final size of code when loaded. */
-    byte mod_md5[MD5_SIZE];	/* MD5 for module code. */
-    int may_load_nif;           /* true if NIFs may later be loaded for this module */  
-    int on_load;		/* Index in the code for the on_load function
-				 * (or 0 if there is no on_load function)
-				 */
-    int otp_20_or_higher;       /* Compiled with OTP 20 or higher */
-    unsigned max_opcode;        /* Highest opcode used in module */
-
-    /*
-     * Atom table.
-     */
-
-    unsigned int num_atoms;	/* Number of atoms in atom table. */
-    Eterm* atom;		/* Atom table. */
-
-    unsigned int num_exps;	/* Number of exports. */
-    ExportEntry* export;	/* Pointer to export table. */
-
-    unsigned int num_imports;   /* Number of imports. */
-    ImportEntry* import;	/* Import entry (translated information). */
-
-    /*
-     * Generic instructions.
-     */
-    GenOp* genop;		/* The last generic instruction seen. */
-    GenOp* free_genop;		/* List of free genops. */
-    GenOpBlock* genop_blocks;	/* List of all block of allocated genops. */
-
-    /*
-     * Lambda table.
-     */
-
-    unsigned int num_lambdas;	/* Number of lambdas in table. */
-    unsigned int lambdas_allocated; /* Size of allocated lambda table. */
-    Lambda* lambdas;		/* Pointer to lambdas. */
-    Lambda def_lambdas[16];	/* Default storage for lambda table. */
-    char* lambda_error;		/* Delayed missing 'FunT' error. */
-
-    /*
-     * Literals (constant pool).
-     */
-
-    unsigned int num_literals;	/* Number of literals in table. */
-    unsigned int allocated_literals; /* Number of literal entries allocated. */
-    Literal* literals;		/* Array of literals. */
-    LiteralPatch* literal_patches; /* Operands that need to be patched. */
-    Uint total_literal_size;	/* Total heap size for all literals. */
-
-    /*
-     * Line table.
-     */
-    BeamInstr* line_item;	/* Line items from the BEAM file. */
-    unsigned int num_line_items;/* Number of line items. */
-    LineInstr* line_instr;	/* Line instructions */
-    unsigned int num_line_instrs; /* Maximum number of line instructions */
-    unsigned int current_li;	/* Current line instruction */
-    unsigned int* func_line;	/* Mapping from function to first line instr */
-    Eterm* fname;		/* List of file names */
-    unsigned int num_fnames;	/* Number of filenames in fname table */
-    int loc_size;		/* Size of location info in bytes (2/4) */
-} LoaderState;
-
 #define GetTagAndValue(Stp, Tag, Val)					\
    do {									\
       BeamInstr __w;							\
@@ -458,25 +189,7 @@ typedef struct LoaderState {
        LoadError2((Stp), "bad atom index %d in %s", (Index), stp->file_name); \
     }
 
-#ifdef DEBUG
-# define GARBAGE 0xCC
-# define DEBUG_INIT_GENOP(Dst) sys_memset(Dst, GARBAGE, sizeof(GenOp))
-#else
-# define DEBUG_INIT_GENOP(Dst)
-#endif
-
-#define NEW_GENOP(Stp, Dst) \
-  do { \
-    if ((Stp)->free_genop == NULL) { \
-       new_genop((Stp)); \
-    } \
-   Dst = (Stp)->free_genop; \
-   (Stp)->free_genop = (Stp)->free_genop->next; \
-   DEBUG_INIT_GENOP(Dst); \
-   (Dst)->a = (Dst)->def_args; \
-  } while (0)
-
-#define FREE_GENOP(Stp, Genop) \
+#define FREE_GENOP(Stp, Genop)                 \
  do { \
    if ((Genop)->a != (Genop)->def_args) { \
        erts_free(ERTS_ALC_T_LOADER_TMP, (Genop)->a); \
@@ -485,20 +198,6 @@ typedef struct LoaderState {
    (Stp)->free_genop = (Genop); \
  } while (0)
 
-#define GENOP_ARITY(Genop, Arity) \
-  do { \
-   ASSERT((Genop)->a == (Genop)->def_args); \
-   (Genop)->arity = (Arity); \
-   (Genop)->a = erts_alloc(ERTS_ALC_T_LOADER_TMP, \
-			   (Genop)->arity * sizeof(GenOpArg)); \
-  } while (0)
-
-#define GENOP_NAME_ARITY(Genop, Name, Arity)    \
-  do {                                          \
-    (Genop)->op = genop_##Name##_##Arity;       \
-    (Genop)->arity = Arity;                     \
-  } while (0)
-
 static void free_loader_state(Binary* magic);
 static ErlHeapFragment* new_literal_fragment(Uint size);
 static void free_literal_fragment(ErlHeapFragment*);
@@ -523,38 +222,17 @@ static int read_line_table(LoaderState* stp);
 static int read_code_header(LoaderState* stp);
 static void init_label(Label* lp);
 static int load_code(LoaderState* stp);
-static GenOp* gen_element(LoaderState* stp, GenOpArg Fail, GenOpArg Index,
-			  GenOpArg Tuple, GenOpArg Dst);
-static GenOp* gen_split_values(LoaderState* stp, GenOpArg S,
-			       GenOpArg TypeFail, GenOpArg Fail,
-			       GenOpArg Size, GenOpArg* Rest);
-static GenOp* gen_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail,
-			     GenOpArg Size, GenOpArg* Rest);
-static GenOp* gen_select_literals(LoaderState* stp, GenOpArg S,
-				  GenOpArg Fail, GenOpArg Size,
-				  GenOpArg* Rest);
-static GenOp* const_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail,
-			       GenOpArg Size, GenOpArg* Rest);
-
-static GenOp* gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
-                                  GenOpArg Size, GenOpArg* Rest);
 
 static int freeze_code(LoaderState* stp);
 
 static void final_touch(LoaderState* stp, struct erl_module_instance* inst_p);
 static void short_file(int line, LoaderState* stp, unsigned needed);
 static void load_printf(int line, LoaderState* context, char *fmt, ...);
-static int transform_engine(LoaderState* st);
 static void id_to_string(Uint id, char* s);
-static void new_genop(LoaderState* stp);
 static int get_tag_and_value(LoaderState* stp, Uint len_code,
 			     unsigned tag, BeamInstr* result);
-static int new_label(LoaderState* stp);
 static void new_literal_patch(LoaderState* stp, int pos);
 static void new_string_patch(LoaderState* stp, int pos);
-static int find_literal(LoaderState* stp, Eterm needle, Uint *idx);
-static Uint new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size);
-static int genopargcompare(GenOpArg* a, GenOpArg* b);
 static Eterm get_module_info(Process* p, ErtsCodeIndex code_ix,
                              BeamCodeHeader*, Eterm module, Eterm what);
 static Eterm exported_from_module(Process* p, ErtsCodeIndex code_ix,
@@ -566,7 +244,6 @@ static Eterm compilation_info_for_module(Process* p, BeamCodeHeader*);
 static Eterm md5_of_module(Process* p, BeamCodeHeader*);
 static Eterm has_native(BeamCodeHeader*);
 static Eterm native_addresses(Process* p, BeamCodeHeader*);
-static int safe_mul(UWord a, UWord b, UWord* resp);
 
 static int must_swap_floats;
 
@@ -653,7 +330,7 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader,
     CHKALLOC();
     CHKBLK(ERTS_ALC_T_CODE,stp->code);
     if (!init_iff_file(stp, code, unloaded_size) ||
-	!scan_iff_file(stp, chunk_types, NUM_CHUNK_TYPES) ||
+	!scan_iff_file(stp, chunk_types, ERTS_BEAM_NUM_CHUNK_TYPES) ||
 	!verify_chunks(stp)) {
 	goto load_error;
     }
@@ -2027,7 +1704,7 @@ load_code(LoaderState* stp)
 
 	arity = gen_opc[last_op->op].arity;
 	last_op->arity = 0;
-	ASSERT(arity <= MAX_OPARGS);
+	ASSERT(arity <= ERTS_BEAM_MAX_OPARGS);
 
 	for (arg = 0; arg < arity; arg++) {
 	    GetTagAndValue(stp, last_op->a[arg].type, last_op->a[arg].val);
@@ -2177,7 +1854,7 @@ load_code(LoaderState* stp)
 		 */
 		goto get_next_instr;
 	    }
-	    switch (transform_engine(stp)) {
+	    switch (erts_transform_engine(stp)) {
 	    case TE_FAIL:
 		/*
 		 * No transformation found. stp->genop != NULL and
@@ -2585,7 +2262,7 @@ load_code(LoaderState* stp)
                         sp->patch_pos = 0;
 
                         for (lp = stp->literal_patches;
-                             lp && lp->pos > ci-MAX_OPARGS;
+                             lp && lp->pos > ci - ERTS_BEAM_MAX_OPARGS;
                              lp = lp->next) {
                             if (lp->pos == ci) {
                                 sp->patch_pos = &lp->pos;
@@ -2981,2008 +2658,116 @@ load_code(LoaderState* stp)
     return retval;
 }
 
-#define succ(St, X, Y) ((X).type == (Y).type && (X).val + 1 == (Y).val)
-#define succ2(St, X, Y) ((X).type == (Y).type && (X).val + 2 == (Y).val)
-#define succ3(St, X, Y) ((X).type == (Y).type && (X).val + 3 == (Y).val)
-#define succ4(St, X, Y) ((X).type == (Y).type && (X).val + 4 == (Y).val)
-
-#define offset(St, X, Y, Offset) ((X).type == (Y).type && (X).val + Offset == (Y).val)
-
-#ifdef NO_FPE_SIGNALS 
-#define no_fpe_signals(St) 1
-#else
-#define no_fpe_signals(St) 0
-#endif
-
-#define never(St) 0
-
-static int
-compiled_with_otp_20_or_higher(LoaderState* stp)
-{
-    return stp->otp_20_or_higher;
-}
-
 /*
- * Predicate that tests whether the following two moves are independent:
- *
- *    move Src1 Dst1
- *    move Src2 Dst2
- *
+ * Freeze the code in memory, move the string table into place,
+ * resolve all labels.
  */
-static int
-independent_moves(LoaderState* stp, GenOpArg Src1, GenOpArg Dst1,
-                  GenOpArg Src2, GenOpArg Dst2)
-{
-    return (Src1.type != Dst2.type || Src1.val != Dst2.val) &&
-        (Src2.type != Dst1.type || Src2.val != Dst1.val) &&
-        (Dst1.type != Dst2.type ||Dst1.val != Dst2.val);
-}
 
-/*
- * Predicate that tests that two registers are distinct.
- *
- *    move Src1 Dst1
- *    move Src2 Dst2
- *
- */
 static int
-distinct(LoaderState* stp, GenOpArg Reg1, GenOpArg Reg2)
+freeze_code(LoaderState* stp)
 {
-    return Reg1.type != Reg2.type || Reg1.val != Reg2.val;
-}
-
-/*
- * Predicate that tests whether a jump table can be used.
- */
+    BeamCodeHeader* code_hdr = stp->hdr;
+    BeamInstr* codev = (BeamInstr*) &stp->hdr->functions;
+    int i;
+    byte* str_table;
+    unsigned strtab_size = stp->chunks[STR_CHUNK].size;
+    unsigned attr_size = stp->chunks[ATTR_CHUNK].size;
+    unsigned compile_size = stp->chunks[COMPILE_CHUNK].size;
+    Uint size;
+    Sint decoded_size;
+    Uint line_size;
 
-static int
-use_jump_tab(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
-{
-    Sint min, max;
-    Sint i;
+    /*
+     * Verify that there was a correct 'FunT' chunk if there were
+     * make_fun2 instructions in the file.
+     */
 
-    if (Size.val < 2 || Size.val % 2 != 0) {
-	return 0;
+    if (stp->lambda_error != NULL) {
+	LoadError0(stp, stp->lambda_error);
     }
 
-    /* we may be called with sequences of tagged fixnums or atoms;
-       return early in latter case, before we access the values */
-    if (Rest[0].type != TAG_i || Rest[1].type != TAG_f)
-	return 0;
-    min = max = Rest[0].val;
-    for (i = 2; i < Size.val; i += 2) {
-	if (Rest[i].type != TAG_i || Rest[i+1].type != TAG_f) {
-	    return 0;
-	}
-	if (Rest[i].val < min) {
-	    min = Rest[i].val;
-	} else if (max < Rest[i].val) {
-	    max = Rest[i].val;
-	}
+    /*
+     * Calculate the final size of the code.
+     */
+    if (stp->line_instr == 0) {
+	line_size = 0;
+    } else {
+	line_size = (offsetof(BeamCodeLineTab,func_tab)
+                     + (stp->num_functions + 1) * sizeof(BeamInstr**) /* func_tab */
+                     + (stp->current_li + 1) * sizeof(BeamInstr*)     /* line items */
+                     + stp->num_fnames * sizeof(Eterm)                /* fname table */
+                     + (stp->current_li + 1) * stp->loc_size);        /* loc_tab */
     }
+    size = offsetof(BeamCodeHeader,functions) + (stp->ci * sizeof(BeamInstr)) +
+	strtab_size + attr_size + compile_size + MD5_SIZE + line_size;
 
-    return max - min <= Size.val;
-}
+    /*
+     * Move the code to its final location.
+     */
 
-/*
- * Predicate to test whether all values in a table are either
- * floats or bignums.
- */
+    code_hdr = (BeamCodeHeader*) erts_realloc(ERTS_ALC_T_CODE, (void *) code_hdr, size);
+    codev = (BeamInstr*) &code_hdr->functions;
+    CHKBLK(ERTS_ALC_T_CODE,code_hdr);
+    /*
+     * Place a pointer to the op_int_code_end instruction in the
+     * function table in the beginning of the file.
+     */
 
-static int
-floats_or_bignums(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
-{
-    int i;
+    code_hdr->functions[stp->num_functions] = (ErtsCodeInfo*)(codev + stp->ci - 1);
+    CHKBLK(ERTS_ALC_T_CODE,code_hdr);
 
-    if (Size.val < 2 || Size.val % 2 != 0) {
-	return 0;
-    }
+    /*
+     * Store the pointer to the on_load function.
+     */
 
-    for (i = 0; i < Size.val; i += 2) {
-	if (Rest[i].type != TAG_q) {
-	    return 0;
-	}
-	if (Rest[i+1].type != TAG_f) {
-	    return 0;
-	}
+    if (stp->on_load) {
+	code_hdr->on_load_function_ptr = codev + stp->on_load;
+    } else {
+	code_hdr->on_load_function_ptr = NULL;
     }
+    CHKBLK(ERTS_ALC_T_CODE,code_hdr);
 
-    return 1;
-}
-
-
-/*
- * Predicate to test whether all values in a table have a fixed size.
- */
+    /*
+     * Place the literals in their own allocated heap (for fast range check)
+     * and fix up all instructions that refer to it.
+     */
+    {
+	Eterm* ptr;
+	LiteralPatch* lp;
+        ErlOffHeap code_off_heap;
+	ErtsLiteralArea *literal_area;
+	Uint lit_asize;
 
-static int
-fixed_size_values(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
-{
-    int i;
+        ERTS_INIT_OFF_HEAP(&code_off_heap);
 
-    if (Size.val < 2 || Size.val % 2 != 0) {
-	return 0;
-    }
+	lit_asize = ERTS_LITERAL_AREA_ALLOC_SIZE(stp->total_literal_size);
+	literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_asize);
+	ptr = &literal_area->start[0];
+	literal_area->end = ptr + stp->total_literal_size;
 
-    for (i = 0; i < Size.val; i += 2) {
-	if (Rest[i+1].type != TAG_f)
-	    return 0;
-	switch (Rest[i].type) {
-	case TAG_a:
-	case TAG_i:
-	case TAG_v:
-	    break;
-	case TAG_q:
-	    return is_float(stp->literals[Rest[i].val].term);
-	default:
-	    return 0;
+	for (i = 0; i < stp->num_literals; i++) {
+            if (is_not_immed(stp->literals[i].term)) {
+                erts_move_multi_frags(&ptr, &code_off_heap,
+				      stp->literals[i].heap_frags,
+				      &stp->literals[i].term, 1, 1);
+                ASSERT(erts_is_literal(stp->literals[i].term,
+				       ptr_val(stp->literals[i].term)));
+            }
 	}
-    }
-
-    return 1;
-}
-
-static int
-mixed_types(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
-{
-    int i;
-    Uint type;
-
-    if (Size.val < 2 || Size.val % 2 != 0) {
-	return 0;
-    }
+	literal_area->off_heap = code_off_heap.first;
+	lp = stp->literal_patches;
+	while (lp != 0) {
+	    BeamInstr* op_ptr;
+	    Literal* lit;
 
-    type = Rest[0].type;
-    for (i = 0; i < Size.val; i += 2) {
-	if (Rest[i].type != type)
-	    return 1;
+	    op_ptr = codev + lp->pos;
+	    lit = &stp->literals[op_ptr[0]];
+	    op_ptr[0] = lit->term;
+	    lp = lp->next;
+	}
+	code_hdr->literal_area = literal_area;
     }
-
-    return 0;
-}
-
-/*
- * Test whether register Reg is killed by make_fun instruction that
- * creates the fun given by index idx.
- */
-
-static int
-is_killed_by_make_fun(LoaderState* stp, GenOpArg Reg, GenOpArg idx)
-{
-    Uint num_free;
-
-    if (idx.val >= stp->num_lambdas) {
-        /* Invalid index. Ignore the error for now. */
-        return 0;
-    } else {
-        num_free = stp->lambdas[idx.val].num_free;
-        return Reg.type == TAG_x && num_free <= Reg.val;
-    }
-}
-
-/* Test whether Bif is "heavy" and should always go through its export entry */
-static int
-is_heavy_bif(LoaderState* stp, GenOpArg Bif)
-{
-    Export *ep;
-
-    if (Bif.type != TAG_u || Bif.val >= stp->num_imports) {
-        return 0;
-    }
-
-    ep = stp->import[Bif.val].bif;
-
-    if (ep) {
-        return bif_table[ep->bif_number].kind == BIF_KIND_HEAVY;
-    }
-
-    return 0;
-}
-
-/*
- * Generate an instruction for element/2.
- */
-
-static GenOp*
-gen_element(LoaderState* stp, GenOpArg Fail, GenOpArg Index,
-		      GenOpArg Tuple, GenOpArg Dst)
-{
-    GenOp* op;
-
-    NEW_GENOP(stp, op);
-    op->next = NULL;
-
-    if (Index.type == TAG_i && Index.val > 0 &&
-        Index.val <= ERTS_MAX_TUPLE_SIZE &&
-	(Tuple.type == TAG_x || Tuple.type == TAG_y)) {
-	GENOP_NAME_ARITY(op, i_fast_element, 4);
-	op->a[0] = Tuple;
-	op->a[1] = Fail;
-	op->a[2].type = TAG_u;
-	op->a[2].val = Index.val;
-	op->a[3] = Dst;
-    } else {
-        GENOP_NAME_ARITY(op, i_element, 4);
-	op->a[0] = Tuple;
-	op->a[1] = Fail;
-	op->a[2] = Index;
-	op->a[3] = Dst;
-    }
-
-    return op;
-}
-
-static GenOp*
-gen_bs_save(LoaderState* stp, GenOpArg Reg, GenOpArg Index)
-{
-    GenOp* op;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, i_bs_save2, 2);
-    op->a[0] = Reg;
-    op->a[1] = Index;
-    if (Index.type == TAG_u) {
-	op->a[1].val = Index.val+1;
-    } else if (Index.type == TAG_a && Index.val == am_start) {
-	op->a[1].type = TAG_u;
-	op->a[1].val = 0;
-    }
-    op->next = NULL;
-    return op;
-}
-
-static GenOp*
-gen_bs_restore(LoaderState* stp, GenOpArg Reg, GenOpArg Index)
-{
-    GenOp* op;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, i_bs_restore2, 2);
-    op->a[0] = Reg;
-    op->a[1] = Index;
-    if (Index.type == TAG_u) {
-	op->a[1].val = Index.val+1;
-    } else if (Index.type == TAG_a && Index.val == am_start) {
-	op->a[1].type = TAG_u;
-	op->a[1].val = 0;
-    }
-    op->next = NULL;
-    return op;
-}
-
-/*
- * Generate the fastest instruction to fetch an integer from a binary.
- */
-
-static GenOp*
-gen_get_integer2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
-		 GenOpArg Size, GenOpArg Unit,
-		 GenOpArg Flags, GenOpArg Dst)
-{
-    GenOp* op;
-    UWord bits;
-
-    NEW_GENOP(stp, op);
-
-    NATIVE_ENDIAN(Flags);
-    if (Size.type == TAG_i) {
-	if (!safe_mul(Size.val, Unit.val, &bits)) {
-	    goto error;
-	} else if ((Flags.val & BSF_SIGNED) != 0) {
-	    goto generic;
-	} else if (bits == 8) {
-	    GENOP_NAME_ARITY(op, i_bs_get_integer_8, 3);
-            op->a[0] = Ms;
-	    op->a[1] = Fail;
-	    op->a[2] = Dst;
-	} else if (bits == 16 && (Flags.val & BSF_LITTLE) == 0) {
-	    GENOP_NAME_ARITY(op, i_bs_get_integer_16, 3);
-	    op->a[0] = Ms;
-	    op->a[1] = Fail;
-	    op->a[2] = Dst;
-#ifdef ARCH_64
-	} else if (bits == 32 && (Flags.val & BSF_LITTLE) == 0) {
-	    GENOP_NAME_ARITY(op, i_bs_get_integer_32, 3);
-	    op->a[0] = Ms;
-	    op->a[1] = Fail;
-	    op->a[2] = Dst;
-#endif
-	} else {
-	generic:
-	    if (bits < SMALL_BITS) {
-		GENOP_NAME_ARITY(op, i_bs_get_integer_small_imm, 5);
-		op->a[0] = Ms;
-		op->a[1].type = TAG_u;
-		op->a[1].val = bits;
-		op->a[2] = Fail;
-		op->a[3] = Flags;
-		op->a[4] = Dst;
-	    } else {
-		GENOP_NAME_ARITY(op, i_bs_get_integer_imm, 6);
-		op->a[0] = Ms;
-		op->a[1].type = TAG_u;
-		op->a[1].val = bits;
-		op->a[2] = Live;
-		op->a[3] = Fail;
-		op->a[4] = Flags;
-		op->a[5] = Dst;
-	    }
-	}
-    } else if (Size.type == TAG_q) {
-	Eterm big = stp->literals[Size.val].term;
-	Uint bigval;
-
-	if (!term_to_Uint(big, &bigval)) {
-	error:
-	    GENOP_NAME_ARITY(op, jump, 1);
-	    op->a[0] = Fail;
-	} else {
-	    if (!safe_mul(bigval, Unit.val, &bits)) {
-		goto error;
-	    }
-	    goto generic;
-	}
-    } else if (Size.type == TAG_x || Size.type == TAG_y) {
-        GENOP_NAME_ARITY(op, i_bs_get_integer, 6);
-	op->a[0] = Ms;
-	op->a[1] = Fail;
-	op->a[2] = Live;
-	op->a[3].type = TAG_u;
-	op->a[3].val = (Unit.val << 3) | Flags.val;
-	op->a[4] = Size;
-	op->a[5] = Dst;
-	op->next = NULL;
-	return op;
-    } else {
-        /* Invalid literal size. */
-        goto error;
-    }
-    op->next = NULL;
-    return op;
-}
-
-/*
- * Generate the fastest instruction to fetch a binary from a binary.
- */
-
-static GenOp*
-gen_get_binary2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
-		GenOpArg Size, GenOpArg Unit,
-		GenOpArg Flags, GenOpArg Dst)
-{
-    GenOp* op;
-    NEW_GENOP(stp, op);
-
-    NATIVE_ENDIAN(Flags);
-    if (Size.type == TAG_a && Size.val == am_all) {
-	GENOP_NAME_ARITY(op, i_bs_get_binary_all2, 5);
-	op->a[0] = Ms;
-	op->a[1] = Fail;
-	op->a[2] = Live;
-	op->a[3] = Unit;
-	op->a[4] = Dst;
-    } else if (Size.type == TAG_i) {
-	GENOP_NAME_ARITY(op, i_bs_get_binary_imm2, 6);
-	op->a[0] = Ms;
-	op->a[1] = Fail;
-	op->a[2] = Live;
-	op->a[3].type = TAG_u;
-	if (!safe_mul(Size.val, Unit.val, &op->a[3].val)) {
-	    goto error;
-	}
-	op->a[4] = Flags;
-	op->a[5] = Dst;
-    } else if (Size.type == TAG_q) {
-	Eterm big = stp->literals[Size.val].term;
-	Uint bigval;
-
-	if (!term_to_Uint(big, &bigval)) {
-	error:
-	    GENOP_NAME_ARITY(op, jump, 1);
-	    op->a[0] = Fail;
-	} else {
-	    GENOP_NAME_ARITY(op, i_bs_get_binary_imm2, 6);
-	    op->a[0] = Ms;
-	    op->a[1] = Fail;
-	    op->a[2] = Live;
-	    op->a[3].type = TAG_u;
-	    if (!safe_mul(bigval, Unit.val, &op->a[3].val)) {
-		goto error;
-	    }
-	    op->a[4] = Flags;
-	    op->a[5] = Dst;
-	}
-    } else if (Size.type == TAG_x || Size.type == TAG_y) {
-	GENOP_NAME_ARITY(op, i_bs_get_binary2, 6);
-	op->a[0] = Ms;
-	op->a[1] = Fail;
-	op->a[2] = Live;
-	op->a[3] = Size;
-	op->a[4].type = TAG_u;
-	op->a[4].val = (Unit.val << 3) | Flags.val;
-	op->a[5] = Dst;
-    } else {
-        /* Invalid literal size. */
-        goto error;
-    }
-    op->next = NULL;
-    return op;
-}
-
-/*
- * Predicate to test whether a binary construction is too big.
- */
-
-static int
-binary_too_big(LoaderState* stp, GenOpArg Size)
-{
-    return Size.type == TAG_o ||
-	(Size.type == TAG_u && ((Size.val >> (8*sizeof(Uint)-3)) != 0));
-}
-
-static GenOp*
-gen_put_binary(LoaderState* stp, GenOpArg Fail,GenOpArg Size,
-	       GenOpArg Unit, GenOpArg Flags, GenOpArg Src)
-{
-    GenOp* op;
-    NEW_GENOP(stp, op);
-
-    NATIVE_ENDIAN(Flags);
-    if (Size.type == TAG_a && Size.val == am_all) {
-	GENOP_NAME_ARITY(op, i_new_bs_put_binary_all, 3);
-	op->a[0] = Src;
-	op->a[1] = Fail;
-	op->a[2] = Unit;
-    } else if (Size.type == TAG_i) {
-	GENOP_NAME_ARITY(op, i_new_bs_put_binary_imm, 3);
-	op->a[0] = Fail;
-	op->a[1].type = TAG_u;
-	if (safe_mul(Size.val, Unit.val, &op->a[1].val)) {
-	    op->a[2] = Src;
-	} else {
-        error:
-            GENOP_NAME_ARITY(op, badarg, 1);
-	    op->a[0] = Fail;
-	}
-    } else if (Size.type == TAG_q) {
-#ifdef ARCH_64
-        /*
-         * There is no way that this binary would fit in memory.
-         */
-        goto error;
-#else
-	Eterm big = stp->literals[Size.val].term;
-	Uint bigval;
-        Uint size;
-
-	if (!term_to_Uint(big, &bigval) ||
-            !safe_mul(bigval, Unit.val, &size)) {
-	    goto error;
-	}
-        GENOP_NAME_ARITY(op, i_new_bs_put_binary_imm, 3);
-        op->a[0] = Fail;
-        op->a[1].type = TAG_u;
-        op->a[1].val = size;
-        op->a[2] = Src;
-#endif
-    } else {
-	GENOP_NAME_ARITY(op, i_new_bs_put_binary, 4);
-	op->a[0] = Fail;
-	op->a[1] = Size;
-	op->a[2].type = TAG_u;
-	op->a[2].val = (Unit.val << 3) | (Flags.val & 7);
-	op->a[3] = Src;
-    }
-
-    op->next = NULL;
-    return op;
-}
-
-static GenOp*
-gen_put_integer(LoaderState* stp, GenOpArg Fail, GenOpArg Size,
-		GenOpArg Unit, GenOpArg Flags, GenOpArg Src)
-{
-    GenOp* op;
-    NEW_GENOP(stp, op);
-
-    NATIVE_ENDIAN(Flags);
-	/* Negative size must fail */
-    if (Size.type == TAG_i) {
-        Uint size;
-	if (!safe_mul(Size.val, Unit.val, &size)) {
-        error:
-            GENOP_NAME_ARITY(op, badarg, 1);
-            op->a[0] = Fail;
-            op->next = NULL;
-            return op;
-	}
-	GENOP_NAME_ARITY(op, i_new_bs_put_integer_imm, 4);
-	op->a[0] = Src;
-	op->a[1] = Fail;
-	op->a[2].type = TAG_u;
-	op->a[2].val = size;
-	op->a[3].type = Flags.type;
-	op->a[3].val = (Flags.val & 7);
-    } else if (Size.type == TAG_q) {
-	Eterm big = stp->literals[Size.val].term;
-	Uint bigval;
-        Uint size;
-
-	if (!term_to_Uint(big, &bigval) ||
-            !safe_mul(bigval, Unit.val, &size)) {
-	    goto error;
-	}
-	GENOP_NAME_ARITY(op, i_new_bs_put_integer_imm, 4);
-	op->a[0] = Src;
-	op->a[1] = Fail;
-	op->a[2].type = TAG_u;
-	op->a[2].val = size;
-	op->a[3].type = Flags.type;
-	op->a[3].val = (Flags.val & 7);
-    } else {
-	GENOP_NAME_ARITY(op, i_new_bs_put_integer, 4);
-	op->a[0] = Fail;
-	op->a[1] = Size;
-	op->a[2].type = TAG_u;
-	op->a[2].val = (Unit.val << 3) | (Flags.val & 7);
-	op->a[3] = Src;
-    }
-    op->next = NULL;
-    return op;
-}
-
-static GenOp*
-gen_put_float(LoaderState* stp, GenOpArg Fail, GenOpArg Size,
-	      GenOpArg Unit, GenOpArg Flags, GenOpArg Src)
-{
-    GenOp* op;
-    NEW_GENOP(stp, op);
-
-    NATIVE_ENDIAN(Flags);
-    if (Size.type == TAG_i) {
-	GENOP_NAME_ARITY(op, i_new_bs_put_float_imm, 4);
-	op->a[0] = Fail;
-	op->a[1].type = TAG_u;
-	if (!safe_mul(Size.val, Unit.val, &op->a[1].val)) {
-	    GENOP_NAME_ARITY(op, badarg, 1);
-	    op->a[0] = Fail;
-	} else {
-	    op->a[2] = Flags;
-	    op->a[3] = Src;
-	}
-    } else {
-	GENOP_NAME_ARITY(op, i_new_bs_put_float, 4);
-	op->a[0] = Fail;
-	op->a[1] = Size;
-	op->a[2].type = TAG_u;
-	op->a[2].val = (Unit.val << 3) | (Flags.val & 7);
-	op->a[3] = Src;
-    }
-    op->next = NULL;
-    return op;
-}
-
-/*
- * Generate an instruction to fetch a float from a binary.
- */
-
-static GenOp*
-gen_get_float2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
-		GenOpArg Size, GenOpArg Unit, GenOpArg Flags, GenOpArg Dst)
-{
-    GenOp* op;
-    NEW_GENOP(stp, op);
-
-    NATIVE_ENDIAN(Flags);
-    GENOP_NAME_ARITY(op, i_bs_get_float2, 6);
-    op->a[0] = Ms;
-    op->a[1] = Fail;
-    op->a[2] = Live;
-    op->a[3] = Size;
-    op->a[4].type = TAG_u;
-    op->a[4].val = (Unit.val << 3) | Flags.val;
-    op->a[5] = Dst;
-    op->next = NULL;
-    return op;
-}
-
-/*
- * Generate the fastest instruction for bs_skip_bits.
- */
-
-static GenOp*
-gen_skip_bits2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms,
-	       GenOpArg Size, GenOpArg Unit, GenOpArg Flags)
-{
-    GenOp* op;
-
-    NATIVE_ENDIAN(Flags);
-    NEW_GENOP(stp, op);
-    if (Size.type == TAG_a && Size.val == am_all) {
-        /*
-         * This kind of skip instruction will only be found in modules
-         * compiled before OTP 19. From OTP 19, the compiler generates
-         * a test_unit instruction of a bs_skip at the end of a
-         * binary.
-         *
-         * It is safe to replace the skip instruction with a test_unit
-         * instruction, because the position will never be used again.
-         * If the match context itself is used again, it will be used by
-         * a bs_restore2 instruction which will overwrite the position
-         * by one of the stored positions.
-         */
-	GENOP_NAME_ARITY(op, bs_test_unit, 3);
-	op->a[0] = Fail;
-	op->a[1] = Ms;
-	op->a[2] = Unit;
-    } else if (Size.type == TAG_i) {
-	GENOP_NAME_ARITY(op, i_bs_skip_bits_imm2, 3);
-	op->a[0] = Fail;
-	op->a[1] = Ms;
-	op->a[2].type = TAG_u;
-	if (!safe_mul(Size.val, Unit.val, &op->a[2].val)) {
-	    goto error;
-	}
-    } else if (Size.type == TAG_q) {
-	Eterm big = stp->literals[Size.val].term;
-	Uint bigval;
-
-	if (!term_to_Uint(big, &bigval)) {
-	error:
-	    GENOP_NAME_ARITY(op, jump, 1);
-	    op->a[0] = Fail;
-	} else {
-	    GENOP_NAME_ARITY(op, i_bs_skip_bits_imm2, 3);
-	    op->a[0] = Fail;
-	    op->a[1] = Ms;
-	    op->a[2].type = TAG_u;
-	    if (!safe_mul(bigval, Unit.val, &op->a[2].val)) {
-		goto error;
-	    }
-	}
-    } else if (Size.type == TAG_x || Size.type == TAG_y) {
-	GENOP_NAME_ARITY(op, i_bs_skip_bits2, 4);
-	op->a[0] = Ms;
-	op->a[1] = Size;
-	op->a[2] = Fail;
-	op->a[3] = Unit;
-    } else {
-        /*
-         * Invalid literal size. Can only happen if compiler
-         * optimizations are selectively disabled.  For example,
-         * at the time of writing, [no_copt, no_type_opt] will allow
-         * skip instructions with invalid sizes to slip through.
-         */
-        goto error;
-    }
-    op->next = NULL;
-    return op;
-}
-
-static GenOp*
-gen_increment(LoaderState* stp, GenOpArg Reg,
-              GenOpArg Integer, GenOpArg Dst)
-{
-    GenOp* op;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, i_increment, 3);
-    op->next = NULL;
-    op->a[0] = Reg;
-    op->a[1].type = TAG_u;
-    op->a[1].val = Integer.val;
-    op->a[2] = Dst;
-    return op;
-}
-
-static GenOp*
-gen_increment_from_minus(LoaderState* stp, GenOpArg Reg,
-                         GenOpArg Integer, GenOpArg Dst)
-{
-    GenOp* op;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, i_increment, 3);
-    op->next = NULL;
-    op->a[0] = Reg;
-    op->a[1].type = TAG_u;
-    op->a[1].val = -Integer.val;
-    op->a[2] = Dst;
-    return op;
-}
-
-static GenOp*
-gen_plus_from_minus(LoaderState* stp, GenOpArg Fail, GenOpArg Live,
-                    GenOpArg Src, GenOpArg Integer, GenOpArg Dst)
-{
-    GenOp* op;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, gen_plus, 5);
-    op->next = NULL;
-    op->a[0] = Fail;
-    op->a[1] = Live;
-    op->a[2] = Src;
-    op->a[3].type = TAG_i;
-    op->a[3].val = -Integer.val;
-    op->a[4] = Dst;
-    return op;
-}
-
-/*
- * Test whether the negation of the given number is small.
- */
-static int
-negation_is_small(LoaderState* stp, GenOpArg Int)
-{
-    /* Check for the rare case of overflow in BeamInstr (UWord) -> Sint
-     * Cast to the correct type before using IS_SSMALL (Sint) */
-    return Int.type == TAG_i &&
-           !(Int.val & ~((((BeamInstr)1) << ((sizeof(Sint)*8)-1))-1)) &&
-           IS_SSMALL(-((Sint)Int.val));
-}
-
-/*
- * Mark this label.
- */
-static int
-smp_mark_target_label(LoaderState* stp, GenOpArg L)
-{
-    ASSERT(L.type == TAG_f);
-    stp->labels[L.val].looprec_targeted = 1;
-    return 1;
-}
-
-/*
- * Test whether this label was targeted by a loop_rec/2 instruction.
- */
-
-static int
-smp_already_locked(LoaderState* stp, GenOpArg L)
-{
-    ASSERT(L.type == TAG_u);
-    return stp->labels[L.val].looprec_targeted;
-}
-
-/*
- * Generate a timeout instruction for a literal timeout.
- */
-
-static GenOp*
-gen_literal_timeout(LoaderState* stp, GenOpArg Fail, GenOpArg Time)
-{
-    GenOp* op;
-    Sint timeout;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, wait_timeout_unlocked_int, 2);
-    op->next = NULL;
-    op->a[0].type = TAG_u;
-    op->a[1] = Fail;
-
-    if (Time.type == TAG_i && (timeout = Time.val) >= 0 &&
-#if defined(ARCH_64)
-	(timeout >> 32) == 0
-#else
-	1
-#endif
-	) {
-	op->a[0].val = timeout;
-#if !defined(ARCH_64)
-    } else if (Time.type == TAG_q) {
-	Eterm big;
-
-	big = stp->literals[Time.val].term;
-	if (is_not_big(big)) {
-	    goto error;
-	}
-	if (big_arity(big) > 1 || big_sign(big)) {
-	    goto error;
-	} else {
-	    Uint u;
-	    (void) term_to_Uint(big, &u);
-	    op->a[0].val = (BeamInstr) u;
-	}
-#endif
-    } else {
-#if !defined(ARCH_64)
-    error:
-#endif
-	GENOP_NAME_ARITY(op, i_wait_error, 0);
-    }
-    return op;
-}
-
-static GenOp*
-gen_literal_timeout_locked(LoaderState* stp, GenOpArg Fail, GenOpArg Time)
-{
-    GenOp* op;
-    Sint timeout;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, wait_timeout_locked_int, 2);
-    op->next = NULL;
-    op->a[0].type = TAG_u;
-    op->a[1] = Fail;
-
-    if (Time.type == TAG_i && (timeout = Time.val) >= 0 &&
-#if defined(ARCH_64)
-	(timeout >> 32) == 0
-#else
-	1
-#endif
-	) {
-	op->a[0].val = timeout;
-#if !defined(ARCH_64)
-    } else if (Time.type == TAG_q) {
-	Eterm big;
-
-	big = stp->literals[Time.val].term;
-	if (is_not_big(big)) {
-	    goto error;
-	}
-	if (big_arity(big) > 1 || big_sign(big)) {
-	    goto error;
-	} else {
-	    Uint u;
-	    (void) term_to_Uint(big, &u);
-	    op->a[0].val = (BeamInstr) u;
-	}
-#endif
-    } else {
-#if !defined(ARCH_64)
-    error:
-#endif
-        GENOP_NAME_ARITY(op, i_wait_error_locked, 0);
-    }
-    return op;
-}
-
-/*
- * Tag the list of values with tuple arity tags.
- */
-
-static GenOp*
-gen_select_tuple_arity(LoaderState* stp, GenOpArg S, GenOpArg Fail,
-		       GenOpArg Size, GenOpArg* Rest)
-
-{
-    GenOp* op;
-    GenOpArg *tmp;
-    int arity = Size.val + 3;
-    int size = Size.val / 2;
-    int i, j, align = 0;
-
-    /*
-     * Verify the validity of the list.
-     */
-
-    if (Size.val % 2 != 0)
-	return NULL;
-    for (i = 0; i < Size.val; i += 2) {
-	if (Rest[i].type != TAG_u || Rest[i+1].type != TAG_f) {
-	    return NULL;
-	}
-    }
-
-    /*
-     * Use a special-cased instruction if there are only two values.
-     */
-    if (size == 2) {
-	NEW_GENOP(stp, op);
-	GENOP_NAME_ARITY(op, i_select_tuple_arity2, 4);
-	GENOP_ARITY(op, arity - 1);
-	op->next = NULL;
-	op->a[0] = S;
-	op->a[1] = Fail;
-	op->a[2].type = TAG_u;
-	op->a[2].val  = Rest[0].val;
-	op->a[3].type = TAG_u;
-	op->a[3].val  = Rest[2].val;
-	op->a[4] = Rest[1];
-	op->a[5] = Rest[3];
-
-	return op;
-    }
-
-    /*
-     * Generate the generic instruction.
-     * Assumption:
-     *   Few different tuple arities to select on (fewer than 20).
-     *   Use linear scan approach.
-     */
-
-    align = 1;
-
-    arity += 2*align;
-    size  += align;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, i_select_tuple_arity, 3);
-    GENOP_ARITY(op, arity);
-    op->next = NULL;
-    op->a[0] = S;
-    op->a[1] = Fail;
-    op->a[2].type = TAG_u;
-    op->a[2].val = size;
-
-    tmp = (GenOpArg *) erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(GenOpArg)*(arity-2*align));
-
-    for (i = 3; i < arity - 2*align; i+=2) {
-	tmp[i-3].type = TAG_v;
-	tmp[i-3].val  = make_arityval(Rest[i-3].val);
-	tmp[i-2]      = Rest[i-2];
-    }
-
-    /*
-     * Sort the values to make them useful for a sentinel search
-     */
-
-    qsort(tmp, size - align, 2*sizeof(GenOpArg),
-	    (int (*)(const void *, const void *)) genopargcompare);
-
-    j = 3;
-    for (i = 3; i < arity - 2*align; i += 2) {
-	op->a[j]        = tmp[i-3];
-	op->a[j + size] = tmp[i-2];
-	j++;
-    }
-
-    erts_free(ERTS_ALC_T_LOADER_TMP, (void *) tmp);
-
-    op->a[j].type = TAG_u;
-    op->a[j].val  = ~((BeamInstr)0);
-    op->a[j+size] = Fail;
-
-    return op;
-}
-
-/*
- * Split a list consisting of both small and bignumbers into two
- * select_val instructions.
- */
-
-static GenOp*
-gen_split_values(LoaderState* stp, GenOpArg S, GenOpArg TypeFail,
-		 GenOpArg Fail, GenOpArg Size, GenOpArg* Rest)
-
-{
-    GenOp* op1;
-    GenOp* op2;
-    GenOp* label;
-    GenOp* is_integer;
-    int i;
-
-    ASSERT(Size.val >= 2 && Size.val % 2 == 0);
-
-    NEW_GENOP(stp, is_integer);
-    GENOP_NAME_ARITY(is_integer, is_integer, 2);
-    is_integer->a[0] = TypeFail;
-    is_integer->a[1] = S;
-
-    NEW_GENOP(stp, label);
-    GENOP_NAME_ARITY(label, label, 1);
-    label->a[0].type = TAG_u;
-    label->a[0].val = new_label(stp);
-
-    NEW_GENOP(stp, op1);
-    GENOP_NAME_ARITY(op1, select_val, 3);
-    GENOP_ARITY(op1, 3 + Size.val);
-    op1->a[0] = S;
-    op1->a[1].type = TAG_f;
-    op1->a[1].val = label->a[0].val;
-    op1->a[2].type = TAG_u;
-    op1->a[2].val = 0;
-
-    NEW_GENOP(stp, op2);
-    GENOP_NAME_ARITY(op2, select_val, 3);
-    GENOP_ARITY(op2, 3 + Size.val);
-    op2->a[0] = S;
-    op2->a[1] = Fail;
-    op2->a[2].type = TAG_u;
-    op2->a[2].val = 0;
-
-    /*
-     * Split the list.
-     */
-
-    ASSERT(Size.type == TAG_u);
-    for (i = 0; i < Size.val; i += 2) {
-	GenOp* op = (Rest[i].type == TAG_q) ? op2 : op1;
-	int dst = 3 + op->a[2].val;
-
-	ASSERT(Rest[i+1].type == TAG_f);
-	op->a[dst] = Rest[i];
-	op->a[dst+1] = Rest[i+1];
-	op->arity += 2;
-	op->a[2].val += 2;
-    }
-    ASSERT(op1->a[2].val > 0);
-    ASSERT(op2->a[2].val > 0);
-
-    /*
-     * Order the instruction sequence appropriately.
-     */
-
-    if (TypeFail.val == Fail.val) {
-	/*
-	 * select_val L1 S ... (small numbers)
-	 * label L1
-	 * is_integer Fail S
-	 * select_val Fail S ... (bignums)
-	 */
-	op1->next = label;
-	label->next = is_integer;
-	is_integer->next = op2;
-    } else {
-	/*
-	 * is_integer TypeFail S
-	 * select_val L1 S ... (small numbers)
-	 * label L1
-	 * select_val Fail S ... (bignums)
-	 */
-	is_integer->next = op1;
-	op1->next = label;
-	label->next = op2;
-	op1 = is_integer;
-    }
-    op2->next = NULL;
-
-    return op1;
-}
-
-/*
- * Generate a jump table.
- */
-
-static GenOp*
-gen_jump_tab(LoaderState* stp, GenOpArg S, GenOpArg Fail, GenOpArg Size, GenOpArg* Rest)
-{
-    Sint min, max;
-    Sint i;
-    Sint size;
-    Sint arity;
-    int fixed_args;
-    GenOp* op;
-
-    ASSERT(Size.val >= 2 && Size.val % 2 == 0);
-
-    /*
-     * If there is only one choice, don't generate a jump table.
-     */
-    if (Size.val == 2) {
-	GenOp* jump;
-
-	NEW_GENOP(stp, op);
-	GENOP_NAME_ARITY(op, is_ne_exact, 3);
-	op->a[0] = Rest[1];
-	op->a[1] = S;
-	op->a[2] = Rest[0];
-
-	NEW_GENOP(stp, jump);
-        GENOP_NAME_ARITY(jump, jump, 1);
-	jump->a[0] = Fail;
-
-	op->next = jump;
-	jump->next = NULL;
-	return op;
-    }
-
-    /*
-     * Calculate the minimum and maximum values and size of jump table.
-     */
-
-    ASSERT(Rest[0].type == TAG_i);
-    min = max = Rest[0].val;
-    for (i = 2; i < Size.val; i += 2) {
-	ASSERT(Rest[i].type == TAG_i && Rest[i+1].type == TAG_f);
-	if (Rest[i].val < min) {
-	    min = Rest[i].val;
-	} else if (max < Rest[i].val) {
-	    max = Rest[i].val;
-	}
-    }
-    size = max - min + 1;
-
-    /*
-     * Allocate structure and fill in the fixed fields.
-     */
-
-    NEW_GENOP(stp, op);
-    op->next = NULL;
-    if (min == 0) {
-	GENOP_NAME_ARITY(op, i_jump_on_val_zero, 3);
-    } else {
-	GENOP_NAME_ARITY(op, i_jump_on_val, 4);
-    }
-    fixed_args = op->arity;
-    arity = fixed_args + size;
-    GENOP_ARITY(op, arity);
-    op->a[0] = S;
-    op->a[1] = Fail;
-    op->a[2].type = TAG_u;
-    op->a[2].val = size;
-    op->a[3].type = TAG_u;
-    op->a[3].val = min;
-
-
-    /*
-     * Fill in the jump table.
-     */
-
-    for (i = fixed_args; i < arity; i++) {
-	op->a[i] = Fail;
-    }
-    for (i = 0; i < Size.val; i += 2) {
-	Sint index;
-	index = fixed_args+Rest[i].val-min;
-	ASSERT(fixed_args <= index && index < arity);
-	op->a[index] = Rest[i+1];
-    }
-    return op;
-}
-
-/* 
- *  Compare function for qsort().
- */
-
-static int
-genopargcompare(GenOpArg* a, GenOpArg* b)
-{
-    if (a->val < b->val)
-	return -1;
-    else if (a->val == b->val)
-	return 0;
-    else
-	return 1;
-}
-
-/*
- * Generate a select_val instruction.  We know that a jump table
- * is not suitable, and that all values are of the same type
- * (integer or atoms).
- */
-
-static GenOp*
-gen_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail,
-	       GenOpArg Size, GenOpArg* Rest)
-{
-    GenOp* op;
-    GenOpArg *tmp;
-    int arity = Size.val + 3;
-    int size = Size.val / 2;
-    int i, j, align = 0;
-
-    if (size == 2) {
-	/*
-	 * Use a special-cased instruction if there are only two values.
-	 */
-
-	NEW_GENOP(stp, op);
-	op->next = NULL;
-        GENOP_NAME_ARITY(op, i_select_val2, 4);
-	GENOP_ARITY(op, arity - 1);
-	op->a[0] = S;
-	op->a[1] = Fail;
-	op->a[2] = Rest[0];
-	op->a[3] = Rest[2];
-	op->a[4] = Rest[1];
-	op->a[5] = Rest[3];
-
-	return op;
-    }
-
-    if (size <= 10) {
-        /* Use linear search. Reserve place for a sentinel. */
-        align = 1;
-    }
-
-    arity += 2*align;
-    size  += align;
-
-    NEW_GENOP(stp, op);
-    op->next = NULL;
-    if (align == 0) {
-        GENOP_NAME_ARITY(op, i_select_val_bins, 3);
-    } else {
-        GENOP_NAME_ARITY(op, i_select_val_lins, 3);
-    }
-    GENOP_ARITY(op, arity);
-    op->a[0] = S;
-    op->a[1] = Fail;
-    op->a[2].type = TAG_u;
-    op->a[2].val = size;
-
-    tmp = (GenOpArg *) erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(GenOpArg)*(arity-2*align));
-
-    for (i = 3; i < arity - 2*align; i++) {
-	tmp[i-3] = Rest[i-3];
-    }
-
-    /*
-     * Sort the values to make them useful for a binary or sentinel search.
-     */
-
-    qsort(tmp, size - align, 2*sizeof(GenOpArg),
-	    (int (*)(const void *, const void *)) genopargcompare);
-
-    j = 3;
-    for (i = 3; i < arity - 2*align; i += 2) {
-	op->a[j]      = tmp[i-3];
-	op->a[j+size] = tmp[i-2];
-	j++;
-    }
-
-    erts_free(ERTS_ALC_T_LOADER_TMP, (void *) tmp);
-
-    if (align) {
-        /* Add sentinel for linear search. */
-        op->a[j].type = TAG_u;
-        op->a[j].val  = ~((BeamInstr)0);
-        op->a[j+size] = Fail;
-    }
-
-#ifdef DEBUG
-    for (i = 0; i < size - 1; i++) {
-	ASSERT(op->a[i+3].val <= op->a[i+4].val);
-    }
-#endif
-
-    return op;
-}
-
-/*
- * Generate a select_val instruction for big numbers.
- */
-
-static GenOp*
-gen_select_literals(LoaderState* stp, GenOpArg S, GenOpArg Fail,
-	       GenOpArg Size, GenOpArg* Rest)
-{
-    GenOp* op;
-    GenOp* jump;
-    GenOp** prev_next = &op;
-
-    int i;
-
-    for (i = 0; i < Size.val; i += 2) {
-	GenOp* op;
-	ASSERT(Rest[i].type == TAG_q);
-
-	NEW_GENOP(stp, op);
-	GENOP_NAME_ARITY(op, is_ne_exact, 3);
-	op->a[0] = Rest[i+1];
-	op->a[1] = S;
-	op->a[2] = Rest[i];
-	*prev_next = op;
-	prev_next = &op->next;
-    }
-
-    NEW_GENOP(stp, jump);
-    GENOP_NAME_ARITY(jump, jump, 1);
-    jump->next = NULL;
-    jump->a[0] = Fail;
-    *prev_next = jump;
-    return op;
-}
-
-
-/*
- * Replace a select_val instruction with a constant controlling expression
- * with a jump instruction.
- */
-
-static GenOp*
-const_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail,
-		 GenOpArg Size, GenOpArg* Rest)
-{
-    GenOp* op;
-    int i;
-
-    ASSERT(Size.type == TAG_u);
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, jump, 1);
-    op->next = NULL;
-
-    /*
-     * Search for a literal matching the controlling expression.
-     */
-
-    switch (S.type) {
-    case TAG_q:
-	{
-	    Eterm expr = stp->literals[S.val].term;
-	    for (i = 0; i < Size.val; i += 2) {
-		if (Rest[i].type == TAG_q) {
-		    Eterm term = stp->literals[Rest[i].val].term;
-		    if (eq(term, expr)) {
-			ASSERT(Rest[i+1].type == TAG_f);
-			op->a[0] = Rest[i+1];
-			return op;
-		    }
-		}
-	    }
-	}
-	break;
-    case TAG_i:
-    case TAG_a:
-	for (i = 0; i < Size.val; i += 2) {
-	    if (Rest[i].val == S.val && Rest[i].type == S.type) {
-		ASSERT(Rest[i+1].type == TAG_f);
-		op->a[0] = Rest[i+1];
-		return op;
-	    }
-	}
-	break;
-    }
-
-    /*
-     * No match.  Use the failure label.
-     */
-
-    op->a[0] = Fail;
-    return op;
-}
-
-static GenOp*
-gen_make_fun2(LoaderState* stp, GenOpArg idx)
-{
-    ErlFunEntry* fe;
-    GenOp* op;
-    Uint arity, num_free;
-
-    if (idx.val >= stp->num_lambdas) {
-        stp->lambda_error = "missing or short chunk 'FunT'";
-        fe = 0;
-        num_free = 0;
-        arity = 0;
-    } else {
-        fe = stp->lambdas[idx.val].fe;
-        num_free = stp->lambdas[idx.val].num_free;
-        arity = fe->arity;
-    }
-
-    NEW_GENOP(stp, op);
-
-    /*
-     * It's possible this is called before init process is started,
-     * skip the optimisation in such case.
-     */
-    if (num_free == 0 && erts_init_process_id != ERTS_INVALID_PID) {
-        Uint lit;
-        Eterm* hp;
-        ErlFunThing* funp;
-
-        lit = new_literal(stp, &hp, ERL_FUN_SIZE);
-        funp = (ErlFunThing *) hp;
-        erts_refc_inc(&fe->refc, 2);
-        funp->thing_word = HEADER_FUN;
-        funp->next = NULL;
-        funp->fe = fe;
-        funp->num_free = 0;
-        funp->creator = erts_init_process_id;
-        funp->arity = arity;
-
-        /*
-         * Use a move_fun/2 instruction to load the fun to enable
-         * further optimizations.
-         */
-        GENOP_NAME_ARITY(op, move_fun, 2);
-        op->a[0].type = TAG_q;
-        op->a[0].val = lit;
-        op->a[1].type = TAG_x;
-        op->a[1].val = 0;
-    } else {
-        GENOP_NAME_ARITY(op, i_make_fun, 2);
-        op->a[0].type = TAG_u;
-        op->a[0].val = (BeamInstr) fe;
-        op->a[1].type = TAG_u;
-        op->a[1].val = num_free;
-    }
-
-    op->next = NULL;
-    return op;
-}
-
-static GenOp*
-gen_is_function2(LoaderState* stp, GenOpArg Fail, GenOpArg Fun, GenOpArg Arity)
-{
-    GenOp* op;
-    int literal_arity =  Arity.type == TAG_i;
-    int fun_is_reg = Fun.type == TAG_x || Fun.type == TAG_y;
-
-    NEW_GENOP(stp, op);
-    op->next = NULL;
-
-    if (fun_is_reg &&literal_arity) {
-        /*
-         * Most common case. Fun in a register and arity
-         * is an integer literal.
-         */
-        if (Arity.val > MAX_ARG) {
-            /* Arity is negative or too big. */
-            GENOP_NAME_ARITY(op, jump, 1);
-            op->a[0] = Fail;
-            return op;
-        } else {
-            GENOP_NAME_ARITY(op, hot_is_function2, 3);
-            op->a[0] = Fail;
-            op->a[1] = Fun;
-            op->a[2].type = TAG_u;
-            op->a[2].val = Arity.val;
-            return op;
-        }
-    } else {
-        /*
-         * Handle extremely uncommon cases by a slower sequence.
-         */
-        GenOp* move_fun;
-        GenOp* move_arity;
-
-        NEW_GENOP(stp, move_fun);
-        NEW_GENOP(stp, move_arity);
-
-        move_fun->next = move_arity;
-        move_arity->next = op;
-
-        GENOP_NAME_ARITY(move_fun, move, 2);
-        move_fun->a[0] = Fun;
-        move_fun->a[1].type = TAG_x;
-        move_fun->a[1].val = 1022;
-
-        GENOP_NAME_ARITY(move_arity, move, 2);
-        move_arity->a[0] = Arity;
-        move_arity->a[1].type = TAG_x;
-        move_arity->a[1].val = 1023;
-
-        GENOP_NAME_ARITY(op, cold_is_function2, 3);
-        op->a[0] = Fail;
-        op->a[1].type = TAG_x;
-        op->a[1].val = 1022;
-        op->a[2].type = TAG_x;
-        op->a[2].val = 1023;
-        return move_fun;
-    }
-}
-
-static GenOp*
-tuple_append_put5(LoaderState* stp, GenOpArg Arity, GenOpArg Dst,
-		  GenOpArg* Puts, GenOpArg S1, GenOpArg S2, GenOpArg S3,
-		  GenOpArg S4, GenOpArg S5)
-{
-    GenOp* op;
-    int arity = Arity.val;	/* Arity of tuple, not the instruction */
-    int i;
-
-    NEW_GENOP(stp, op);
-    op->next = NULL;
-    GENOP_NAME_ARITY(op, i_put_tuple, 2);
-    GENOP_ARITY(op, arity+2+5);
-    op->a[0] = Dst;
-    op->a[1].type = TAG_u;
-    op->a[1].val = arity + 5;
-    for (i = 0; i < arity; i++) {
-	op->a[i+2] = Puts[i];
-    }
-    op->a[arity+2] = S1;
-    op->a[arity+3] = S2;
-    op->a[arity+4] = S3;
-    op->a[arity+5] = S4;
-    op->a[arity+6] = S5;
-    return op;
-}
-
-static GenOp*
-tuple_append_put(LoaderState* stp, GenOpArg Arity, GenOpArg Dst,
-		 GenOpArg* Puts, GenOpArg S)
-{
-    GenOp* op;
-    int arity = Arity.val;	/* Arity of tuple, not the instruction */
-    int i;
-
-    NEW_GENOP(stp, op);
-    op->next = NULL;
-    GENOP_NAME_ARITY(op, i_put_tuple, 2);
-    GENOP_ARITY(op, arity+2+1);
-    op->a[0] = Dst;
-    op->a[1].type = TAG_u;
-    op->a[1].val = arity + 1;
-    for (i = 0; i < arity; i++) {
-	op->a[i+2] = Puts[i];
-    }
-    op->a[arity+2] = S;
-    return op;
-}
-
-/*
- * Predicate to test whether the given literal is a map.
- */
-
-static int
-literal_is_map(LoaderState* stp, GenOpArg Lit)
-{
-    Eterm term;
-
-    ASSERT(Lit.type == TAG_q);
-    term = stp->literals[Lit.val].term;
-    return is_map(term);
-}
-
-/*
- * Predicate to test whether all of the given new small map keys are literals
- */
-static int
-is_small_map_literal_keys(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
-{
-    if (Size.val > MAP_SMALL_MAP_LIMIT) {
-        return 0;
-    }
-
-    /*
-     * Operations with non-literals have always only one key.
-     */
-    if (Size.val != 2) {
-        return 1;
-    }
-
-    switch (Rest[0].type) {
-    case TAG_a:
-    case TAG_i:
-    case TAG_n:
-    case TAG_q:
-        return 1;
-    default:
-        return 0;
-    }
-}
-
-static GenOp*
-gen_new_small_map_lit(LoaderState* stp, GenOpArg Dst, GenOpArg Live,
-                      GenOpArg Size, GenOpArg* Rest)
-{
-    unsigned size = Size.val;
-    Uint lit;
-    unsigned i;
-    GenOp* op;
-    GenOpArg* dst;
-    Eterm* hp;
-    Eterm* tmp;
-    Eterm* thp;
-    Eterm keys;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, i_new_small_map_lit, 3);
-    GENOP_ARITY(op, 3 + size/2);
-    op->next = NULL;
-
-    tmp = thp = erts_alloc(ERTS_ALC_T_LOADER_TMP, (1 + size/2) * sizeof(*tmp));
-    keys = make_tuple(thp);
-    *thp++ = make_arityval(size/2);
-
-    dst = op->a+3;
-
-    for (i = 0; i < size; i += 2) {
-        switch (Rest[i].type) {
-        case TAG_a:
-            *thp++ = Rest[i].val;
-            ASSERT(is_atom(Rest[i].val));
-            break;
-        case TAG_i:
-            *thp++ = make_small(Rest[i].val);
-            break;
-        case TAG_n:
-            *thp++ = NIL;
-            break;
-        case TAG_q:
-            *thp++ = stp->literals[Rest[i].val].term;
-            break;
-        }
-        *dst++ = Rest[i + 1];
-    }
-
-    if (!find_literal(stp, keys, &lit)) {
-        lit = new_literal(stp, &hp, 1 + size/2);
-        sys_memcpy(hp, tmp, (1 + size/2) * sizeof(*tmp));
-    }
-    erts_free(ERTS_ALC_T_LOADER_TMP, tmp);
-
-    op->a[0] = Dst;
-    op->a[1] = Live;
-    op->a[2].type = TAG_q;
-    op->a[2].val = lit;
-
-    return op;
-}
-
-/*
- * Predicate to test whether the given literal is an empty map.
- */
-
-static int
-is_empty_map(LoaderState* stp, GenOpArg Lit)
-{
-    Eterm term;
-
-    if (Lit.type != TAG_q) {
-	return 0;
-    }
-    term = stp->literals[Lit.val].term;
-    return is_flatmap(term) && flatmap_get_size(flatmap_val(term)) == 0;
-}
-
-/*
- * Pseudo predicate map_key_sort that will sort the Rest operand for
- * map instructions as a side effect.
- */
-
-typedef struct SortGenOpArg {
-    Eterm term;			/* Term to use for comparing  */
-    GenOpArg arg;		/* Original data */
-} SortGenOpArg;
-
-static int
-genopargtermcompare(SortGenOpArg* a, SortGenOpArg* b)
-{
-    Sint res = CMP_TERM(a->term, b->term);
-
-    if (res < 0) {
-        return -1;
-    } else if (res > 0) {
-        return 1;
-    }
-
-    return 0;
-}
-
-static int
-map_key_sort(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
-{
-    SortGenOpArg* t;
-    unsigned size = Size.val;
-    unsigned i;
-
-    if (size == 2) {
-	return 1;		/* Already sorted. */
-    }
-
-
-    t = (SortGenOpArg *) erts_alloc(ERTS_ALC_T_TMP, size*sizeof(SortGenOpArg));
-
-    /*
-     * Copy original data and sort keys to a temporary array.
-     */
-    for (i = 0; i < size; i += 2) {
-	t[i].arg = Rest[i];
-	switch (Rest[i].type) {
-	case TAG_a:
-	    t[i].term = Rest[i].val;
-	    ASSERT(is_atom(t[i].term));
-	    break;
-	case TAG_i:
-	    t[i].term = make_small(Rest[i].val);
-	    break;
-	case TAG_n:
-	    t[i].term = NIL;
-	    break;
-	case TAG_q:
-	    t[i].term = stp->literals[Rest[i].val].term;
-	    break;
-	default:
-	    /*
-	     * Not a literal key. Not allowed. Only a single
-	     * variable key is allowed in each map instruction.
-	     */
-	    erts_free(ERTS_ALC_T_TMP, (void *) t);
-	    return 0;
-	}
-#ifdef DEBUG
-	t[i+1].term = THE_NON_VALUE;
-#endif
-	t[i+1].arg = Rest[i+1];
-    }
-
-    /*
-     * Sort the temporary array.
-     */
-    qsort((void *) t, size / 2, 2 * sizeof(SortGenOpArg),
-	  (int (*)(const void *, const void *)) genopargtermcompare);
-
-    /*
-     * Copy back the sorted, original data.
-     */
-    for (i = 0; i < size; i++) {
-	Rest[i] = t[i].arg;
-    }
-
-    erts_free(ERTS_ALC_T_TMP, (void *) t);
-    return 1;
-}
-
-static int
-hash_genop_arg(LoaderState* stp, GenOpArg Key, Uint32* hx)
-{
-    switch (Key.type) {
-    case TAG_a:
-	*hx = hashmap_make_hash(Key.val);
-	return 1;
-    case TAG_i:
-	*hx = hashmap_make_hash(make_small(Key.val));
-	return 1;
-    case TAG_n:
-	*hx = hashmap_make_hash(NIL);
-	return 1;
-    case TAG_q:
-	*hx = hashmap_make_hash(stp->literals[Key.val].term);
-	return 1;
-    default:
-	return 0;
-    }
-}
-
-/*
- * Replace a get_map_elements with one key to an instruction with one
- * element.
- */
-
-static GenOp*
-gen_get_map_element(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
-		    GenOpArg Size, GenOpArg* Rest)
-{
-    GenOp* op;
-    GenOpArg Key;
-    Uint32 hx = 0;
-
-    ASSERT(Size.type == TAG_u);
-
-    NEW_GENOP(stp, op);
-    op->next = NULL;
-    op->a[0] = Fail;
-    op->a[1] = Src;
-    op->a[2] = Rest[0];
-
-    Key = Rest[0];
-    if (hash_genop_arg(stp, Key, &hx)) {
-	GENOP_NAME_ARITY(op, i_get_map_element_hash, 5);
-	op->a[3].type = TAG_u;
-	op->a[3].val = (BeamInstr) hx;
-	op->a[4] = Rest[1];
-    } else {
-	GENOP_NAME_ARITY(op, i_get_map_element, 4);
-	op->a[3] = Rest[1];
-    }
-    return op;
-}
-
-static int
-hash_internal_genop_arg(LoaderState* stp, GenOpArg Key, Uint32* hx)
-{
-    Eterm key_term;
-    switch (Key.type) {
-    case TAG_a:
-	key_term = Key.val;
-        break;
-    case TAG_i:
-	key_term = make_small(Key.val);
-        break;
-    case TAG_n:
-        key_term = NIL;
-        break;
-    case TAG_q:
-	key_term = stp->literals[Key.val].term;
-	break;
-    default:
-	return 0;
-    }
-    *hx = erts_pd_make_hx(key_term);
-    return 1;
-}
-
-
-static GenOp*
-gen_get(LoaderState* stp, GenOpArg Src, GenOpArg Dst)
-{
-    GenOp* op;
-    Uint32 hx = 0;
-
-    NEW_GENOP(stp, op);
-    op->next = NULL;
-    if (hash_internal_genop_arg(stp, Src, &hx)) {
-	GENOP_NAME_ARITY(op, i_get_hash, 3);
-        op->a[0] = Src;
-	op->a[1].type = TAG_u;
-	op->a[1].val = (BeamInstr) hx;
-	op->a[2] = Dst;
-    } else {
-	GENOP_NAME_ARITY(op, i_get, 2);
-        op->a[0] = Src;
-	op->a[1] = Dst;
-    }
-    return op;
-}
-
-
-static GenOp*
-gen_get_map_elements(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
-		     GenOpArg Size, GenOpArg* Rest)
-{
-    GenOp* op;
-    Uint32 hx;
-    Uint i;
-    GenOpArg* dst;
-#ifdef DEBUG
-    int good_hash;
-#endif
-
-    ERTS_UNDEF(hx, 0);
-    ASSERT(Size.type == TAG_u);
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, i_get_map_elements, 3);
-    GENOP_ARITY(op, 3 + 3*(Size.val/2));
-    op->next = NULL;
-    op->a[0] = Fail;
-    op->a[1] = Src;
-    op->a[2].type = TAG_u;
-    op->a[2].val = 3*(Size.val/2);
-
-    dst = op->a+3;
-    for (i = 0; i < Size.val / 2; i++) {
-	dst[0] = Rest[2*i];
-	dst[1] = Rest[2*i+1];
-#ifdef DEBUG
-	good_hash =
-#endif
-	    hash_genop_arg(stp, dst[0], &hx);
-#ifdef DEBUG
-	ASSERT(good_hash);
-#endif
-	dst[2].type = TAG_u;
-	dst[2].val = (BeamInstr) hx;
-	dst += 3;
-    }
-    return op;
-}
-
-static GenOp*
-gen_has_map_fields(LoaderState* stp, GenOpArg Fail, GenOpArg Src,
-		   GenOpArg Size, GenOpArg* Rest)
-{
-    GenOp* op;
-    Uint i;
-    Uint n;
-
-    ASSERT(Size.type == TAG_u);
-    n = Size.val;
-
-    NEW_GENOP(stp, op);
-    GENOP_NAME_ARITY(op, get_map_elements, 3);
-    GENOP_ARITY(op, 3 + 2*n);
-    op->next = NULL;
-
-    op->a[0] = Fail;
-    op->a[1] = Src;
-    op->a[2].type = TAG_u;
-    op->a[2].val = 2*n;
-
-    for (i = 0; i < n; i++) {
-	op->a[3+2*i] = Rest[i];
-	op->a[3+2*i+1].type = TAG_x;
-	op->a[3+2*i+1].val = SCRATCH_X_REG; /* Ignore result */
-    }
-    return op;
-}
-
-/*
- * Freeze the code in memory, move the string table into place,
- * resolve all labels.
- */
-
-static int
-freeze_code(LoaderState* stp)
-{
-    BeamCodeHeader* code_hdr = stp->hdr;
-    BeamInstr* codev = (BeamInstr*) &stp->hdr->functions;
-    int i;
-    byte* str_table;
-    unsigned strtab_size = stp->chunks[STR_CHUNK].size;
-    unsigned attr_size = stp->chunks[ATTR_CHUNK].size;
-    unsigned compile_size = stp->chunks[COMPILE_CHUNK].size;
-    Uint size;
-    Sint decoded_size;
-    Uint line_size;
-
-    /*
-     * Verify that there was a correct 'FunT' chunk if there were
-     * make_fun2 instructions in the file.
-     */
-
-    if (stp->lambda_error != NULL) {
-	LoadError0(stp, stp->lambda_error);
-    }
-
-    /*
-     * Calculate the final size of the code.
-     */
-    if (stp->line_instr == 0) {
-	line_size = 0;
-    } else {
-	line_size = (offsetof(BeamCodeLineTab,func_tab)
-                     + (stp->num_functions + 1) * sizeof(BeamInstr**) /* func_tab */
-                     + (stp->current_li + 1) * sizeof(BeamInstr*)     /* line items */
-                     + stp->num_fnames * sizeof(Eterm)                /* fname table */
-                     + (stp->current_li + 1) * stp->loc_size);        /* loc_tab */
-    }
-    size = offsetof(BeamCodeHeader,functions) + (stp->ci * sizeof(BeamInstr)) +
-	strtab_size + attr_size + compile_size + MD5_SIZE + line_size;
-
-    /*
-     * Move the code to its final location.
-     */
-
-    code_hdr = (BeamCodeHeader*) erts_realloc(ERTS_ALC_T_CODE, (void *) code_hdr, size);
-    codev = (BeamInstr*) &code_hdr->functions;
-    CHKBLK(ERTS_ALC_T_CODE,code_hdr);
-    /*
-     * Place a pointer to the op_int_code_end instruction in the
-     * function table in the beginning of the file.
-     */
-
-    code_hdr->functions[stp->num_functions] = (ErtsCodeInfo*)(codev + stp->ci - 1);
-    CHKBLK(ERTS_ALC_T_CODE,code_hdr);
-
-    /*
-     * Store the pointer to the on_load function.
-     */
-
-    if (stp->on_load) {
-	code_hdr->on_load_function_ptr = codev + stp->on_load;
-    } else {
-	code_hdr->on_load_function_ptr = NULL;
-    }
-    CHKBLK(ERTS_ALC_T_CODE,code_hdr);
-
-    /*
-     * Place the literals in their own allocated heap (for fast range check)
-     * and fix up all instructions that refer to it.
-     */
-    {
-	Eterm* ptr;
-	LiteralPatch* lp;
-        ErlOffHeap code_off_heap;
-	ErtsLiteralArea *literal_area;
-	Uint lit_asize;
-
-        ERTS_INIT_OFF_HEAP(&code_off_heap);
-
-	lit_asize = ERTS_LITERAL_AREA_ALLOC_SIZE(stp->total_literal_size);
-	literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_asize);
-	ptr = &literal_area->start[0];
-	literal_area->end = ptr + stp->total_literal_size;
-
-	for (i = 0; i < stp->num_literals; i++) {
-            if (is_not_immed(stp->literals[i].term)) {
-                erts_move_multi_frags(&ptr, &code_off_heap,
-				      stp->literals[i].heap_frags,
-				      &stp->literals[i].term, 1, 1);
-                ASSERT(erts_is_literal(stp->literals[i].term,
-				       ptr_val(stp->literals[i].term)));
-            }
-	}
-	literal_area->off_heap = code_off_heap.first;
-	lp = stp->literal_patches;
-	while (lp != 0) {
-	    BeamInstr* op_ptr;
-	    Literal* lit;
-
-	    op_ptr = codev + lp->pos;
-	    lit = &stp->literals[op_ptr[0]];
-	    op_ptr[0] = lit->term;
-	    lp = lp->next;
-	}
-	code_hdr->literal_area = literal_area;
-    }
-    CHKBLK(ERTS_ALC_T_CODE,code);
+    CHKBLK(ERTS_ALC_T_CODE,code);
 
     /*
      * If there is line information, place it here.
@@ -5315,417 +3100,6 @@ final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
     }
 }
 
-static int
-transform_engine(LoaderState* st)
-{
-    Uint op;
-    int ap;			/* Current argument. */
-    const Uint* restart; /* Where to restart if current match fails. */
-    GenOpArg var[TE_MAX_VARS];	/* Buffer for variables. */
-    GenOpArg* rest_args = NULL;
-    int num_rest_args = 0;
-    int i;			/* General index. */
-    Uint mask;
-    GenOp* instr;
-    GenOp* first = st->genop;
-    GenOp* keep = NULL;
-    const Uint* pc;
-    static Uint restart_fail[1] = {TOP_fail};
-
-    ASSERT(gen_opc[first->op].transform != -1);
-    restart = op_transform + gen_opc[first->op].transform;
-
- restart:
-    ASSERT(restart != NULL);
-    pc = restart;
-    ASSERT(*pc < NUM_TOPS);	/* Valid instruction? */
-    instr = first;
-
-#ifdef DEBUG
-    restart = NULL;
-#endif
-    ap = 0;
-    for (;;) {
-	op = *pc++;
-
-	switch (op) {
-	case TOP_next_instr:
-	    instr = instr->next;
-	    ap = 0;
-	    if (instr == NULL) {
-		/*
-		 * We'll need at least one more instruction to decide whether
-		 * this combination matches or not.
-		 */
-		return TE_SHORT_WINDOW;
-	    }
-	    if (*pc++ != instr->op)
-		goto restart;
-	    break;
-	case TOP_is_type:
-	    mask = *pc++;
-
-	    ASSERT(ap < instr->arity);
-	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-	    if (((1 << instr->a[ap].type) & mask) == 0)
-		goto restart;
-	    break;
-#if defined(TOP_is_type_next_arg)
-	case TOP_is_type_next_arg:
-	    mask = *pc++;
-	    ASSERT(ap < instr->arity);
-	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-	    if (((1 << instr->a[ap].type) & mask) == 0)
-		goto restart;
-            ap++;
-	    break;
-#endif
-	case TOP_pred:
-	    i = *pc++;
-	    switch (i) {
-#define RVAL i
-#include "beam_pred_funcs.h"
-#undef RVAL
-	    default:
-		ASSERT(0);
-	    }
-	    if (i == 0)
-		goto restart;
-	    break;
-#if defined(TOP_is_eq)
-	case TOP_is_eq:
-	    ASSERT(ap < instr->arity);
-	    if (*pc++ != instr->a[ap].val)
-		goto restart;
-	    break;
-#endif
-	case TOP_is_type_eq:
-	    mask = *pc++;
-
-	    ASSERT(ap < instr->arity);
-	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-	    if (((1 << instr->a[ap].type) & mask) == 0)
-		goto restart;
-	    if (*pc++ != instr->a[ap].val)
-		goto restart;
-	    break;
-#if defined(TOP_is_type_eq_next_arg)
-	case TOP_is_type_eq_next_arg:
-	    mask = *pc++;
-            ASSERT(ap < instr->arity);
-            ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-            if (((1 << instr->a[ap].type) & mask) == 0)
-                goto restart;
-            if (*pc++ != instr->a[ap].val)
-                goto restart;
-            ap++;
-            break;
-#endif
-	case TOP_is_same_var:
-	    ASSERT(ap < instr->arity);
-	    i = *pc++;
-	    ASSERT(i < TE_MAX_VARS);
-	    if (var[i].type != instr->a[ap].type)
-		goto restart;
-	    switch (var[i].type) {
-	    case TAG_n:
-		break;
-	    default:
-		if (var[i].val != instr->a[ap].val)
-		    goto restart;
-	    }
-	    break;
-#if defined(TOP_is_bif)
-	case TOP_is_bif:
-	    {
-		int bif_number = *pc++;
-		
-		/*
-		 * In debug build, the type must be 'u'.
-		 * In a real build, don't match.  (I.e. retain the original
-		 * call instruction, this will work, but it will be a
-		 * slight performance loss.)
-		 */
-
-		ASSERT(instr->a[ap].type == TAG_u);
-		if (instr->a[ap].type != TAG_u)
-		    goto restart;
-
-		/*
-		 * In debug build, the assertion will catch invalid indexes
-		 * immediately.  In a real build, the loader will issue
-		 * an diagnostic later when the instruction is loaded.
-		 */
-
-		i = instr->a[ap].val;
-		ASSERT(i < st->num_imports);
-		if (i >= st->num_imports || st->import[i].bif == NULL)
-		    goto restart;
-                if (bif_number != -1) {
-                    Export *bif = st->import[i].bif;
-                    if (bif->bif_number != bif_number) {
-                        goto restart;
-                    }
-                }
-	    }
-	    break;
-#endif
-#if defined(TOP_is_not_bif)
-	case TOP_is_not_bif:
-	    {
-		pc++;
-		
-		/*
-		 * In debug build, the type must be 'u'.
-		 */
-
-		ASSERT(instr->a[ap].type == TAG_u);
-		if (instr->a[ap].type != TAG_u) {
-		    goto restart;
-		}
-		i = instr->a[ap].val;
-
-		/*
-		 * erlang:apply/2,3 are strange. They exist as (dummy) BIFs
-		 * so that they are included in the export table before
-		 * the erlang module is loaded. They also exist in the erlang
-		 * module as functions. When used in code, a special Beam
-		 * instruction is used.
-		 * 
-		 * Below we specially recognize erlang:apply/2,3 as special.
-		 * This is necessary because after setting a trace pattern on
-		 * them, you cannot no longer see from the export entry that
-		 * they are special.
-		 */
-		if (i < st->num_imports) {
-		    if (st->import[i].bif != NULL ||
-			(st->import[i].module == am_erlang &&
-			 st->import[i].function == am_apply &&
-			 (st->import[i].arity == 2 || st->import[i].arity == 3))) {
-			goto restart;
-		    }
-		}
-	    }
-	    break;
-
-#endif
-#if defined(TOP_is_func)
-	case TOP_is_func:
-	    {
-		Eterm mod = *pc++;
-		Eterm func = *pc++;
-		int arity = *pc++;
-
-		ASSERT(instr->a[ap].type == TAG_u);
-		if (instr->a[ap].type != TAG_u) {
-		    goto restart;
-		}
-		i = instr->a[ap].val;
-		ASSERT(i < st->num_imports);
-		if (i >= st->num_imports || st->import[i].module != mod ||
-		    st->import[i].function != func ||
-		    (arity < MAX_ARG && st->import[i].arity != arity)) {
-		    goto restart;
-		}
-	    }
-	    break;
-#endif
-	case TOP_set_var_next_arg:
-	    ASSERT(ap < instr->arity);
-	    i = *pc++;
-	    ASSERT(i < TE_MAX_VARS);
-	    var[i].type = instr->a[ap].type;
-	    var[i].val = instr->a[ap].val;
-	    ap++;
-	    break;
-#if defined(TOP_is_type_set_var_next_arg)
-	case TOP_is_type_set_var_next_arg:
-            mask = pc[0];
-            i = pc[1];
-	    ASSERT(i < TE_MAX_VARS);
-            ASSERT(ap < instr->arity);
-	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-	    if (((1 << instr->a[ap].type) & mask) == 0)
-		goto restart;
-	    ASSERT(i < TE_MAX_VARS);
-	    var[i] = instr->a[ap];
-	    ap++;
-            pc += 2;
-	    break;
-#endif
-#if defined(TOP_is_type_eq_set_var_next_arg)
-	case TOP_is_type_eq_set_var_next_arg:
-            {
-                Eterm val;
-                mask = pc[0];
-                val = pc[1];
-                i = pc[2];
-                ASSERT(i < TE_MAX_VARS);
-                ASSERT(ap < instr->arity);
-                ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-                if (((1 << instr->a[ap].type) & mask) == 0)
-                    goto restart;
-                if (val != instr->a[ap].val)
-                    goto restart;
-                ASSERT(i < TE_MAX_VARS);
-                var[i] = instr->a[ap];
-                ap++;
-                pc += 3;
-            }
-	    break;
-#endif
-#if defined(TOP_rest_args)
-	case TOP_rest_args:
-	    {
-		int formal_arity = gen_opc[instr->op].arity;
-		num_rest_args = instr->arity - formal_arity;
-		rest_args = instr->a + formal_arity;
-	    }
-	    break;
-#endif
-	case TOP_next_arg:
-	    ap++;
-	    break;
-	case TOP_commit:
-	    instr = instr->next; /* The next_instr was optimized away. */
-	    keep = instr;
-	    break;
-#if defined(TOP_commit_new_instr)
-	case TOP_commit_new_instr:
-            /*
-             * Reuse the last instruction on the left side instead of
-             * allocating a new instruction. Note that this is not
-             * safe if TOP_rest_args has been executed; therefore,
-             * this combined instruction is never used when that is
-             * the case.
-             */
-            ASSERT(instr->a == instr->def_args);
-            keep = instr;
-            instr->op = op = *pc++;
-            instr->arity = gen_opc[op].arity;
-            ap = 0;
-            break;
-#endif
-#if defined(TOP_keep)
-	case TOP_keep:
-	    /* Keep the current instruction unchanged. */
-	    keep = instr;
-	    break;
-#endif
-#if defined(TOP_call_end)
-	case TOP_call_end:
-	    {
-		GenOp** lastp;
-		GenOp* new_instr;
-
-		i = *pc++;
-		switch (i) {
-#define RVAL new_instr
-#include "beam_tr_funcs.h"
-#undef RVAL
-		default:
-		    new_instr = NULL; /* Silence compiler warning. */
-		    ASSERT(0);
-		}
-		if (new_instr == NULL) {
-		    goto restart;
-		}
-
-		lastp = &new_instr;
-		while (*lastp != NULL) {
-		    lastp = &((*lastp)->next);
-		}
-		 
-		keep = instr->next; /* The next_instr was optimized away. */
-		*lastp = keep;
-                instr = new_instr;
-	    }
-	    /* FALLTHROUGH */
-#endif
-	case TOP_end:
-            st->genop = instr;
-	    while (first != keep) {
-		GenOp* next = first->next;
-		FREE_GENOP(st, first);
-		first = next;
-	    }
-	    return TE_OK;
-	case TOP_new_instr:
-	    /*
-	     * Note that the instructions are generated in reverse order.
-	     */
-            {
-                GenOp* new_instr;
-                NEW_GENOP(st, new_instr);
-                new_instr->next = instr;
-                instr = new_instr;
-                instr->op = op = *pc++;
-                instr->arity = gen_opc[op].arity;
-                ap = 0;
-            }
-            break;
-#ifdef TOP_rename
-	case TOP_rename:
-	    instr->op = op = *pc++;
-	    instr->arity = gen_opc[op].arity;
-	    return TE_OK;
-#endif
-	case TOP_store_val_next_arg:
-            instr->a[ap].type = pc[0];
-            instr->a[ap].val = pc[1];
-            ap++;
-            pc += 2;
-            break;
-	case TOP_store_var_next_arg:
-	    i = *pc++;
-	    ASSERT(i < TE_MAX_VARS);
-	    instr->a[ap].type = var[i].type;
-	    instr->a[ap].val = var[i].val;
-	    ap++;
-	    break;
-#if defined(TOP_store_rest_args)
-	case TOP_store_rest_args:
-	    {
-		GENOP_ARITY(instr, instr->arity+num_rest_args);
-		sys_memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg));
-		sys_memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg));
-		ap += num_rest_args;
-	    }
-	    break;
-#endif
-	case TOP_try_me_else:
-	    restart = pc + 1;
-	    restart += *pc++;
-	    ASSERT(*pc < NUM_TOPS); /* Valid instruction? */
-	    break;
-	case TOP_try_me_else_fail:
-	    restart = restart_fail;
-	    break;
-	case TOP_fail:
-	    return TE_FAIL;
-#if defined(TOP_skip_unless)
-	case TOP_skip_unless:
-            /*
-             * Note that the caller of transform_engine() guarantees that
-             * there is always a second instruction available.
-             */
-            ASSERT(instr);
-            if (instr->next->op != pc[0]) {
-                /* The second instruction is wrong. Skip ahead. */
-                pc += pc[1] + 2;
-                ASSERT(*pc < NUM_TOPS); /* Valid instruction? */
-            } else {
-                /* Correct second instruction. */
-                pc += 2;
-            }
-	    break;
-#endif
-	default:
-	    ASSERT(0);
-	}
-    }
-}
-
 static void
 short_file(int line, LoaderState* stp, unsigned needed)
 {
@@ -5871,8 +3245,8 @@ get_tag_and_value(LoaderState* stp, Uint len_code,
 	    return TAG_i;
 	} else {
             tmp_big = small_to_big(val, big_buf);
-            if (!find_literal(stp, tmp_big, result)) {
-                *result = new_literal(stp, &hp, BIG_UINT_HEAP_SIZE);
+            if (!beam_load_find_literal(stp, tmp_big, result)) {
+                *result = beam_load_new_literal(stp, &hp, BIG_UINT_HEAP_SIZE);
                 sys_memcpy(hp, big_buf, BIG_UINT_HEAP_SIZE*sizeof(Eterm));
             }
 	    return TAG_q;
@@ -5943,8 +3317,8 @@ get_tag_and_value(LoaderState* stp, Uint len_code,
      * Create a literal if there is no previous literal with the same value.
      */
 
-    if (!find_literal(stp, tmp_big, result)) {
-        *result = new_literal(stp, &hp, words_needed);
+    if (!beam_load_find_literal(stp, tmp_big, result)) {
+        *result = beam_load_new_literal(stp, &hp, words_needed);
         sys_memcpy(hp, big_buf, words_needed*sizeof(Eterm));
     }
 
@@ -5981,8 +3355,8 @@ id_to_string(Uint id, char* s)
     *s++ = '\0';
 }
 
-static void
-new_genop(LoaderState* stp)
+void
+beam_load_new_genop(LoaderState* stp)
 {
     GenOpBlock* p = (GenOpBlock *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
 					      sizeof(GenOpBlock));
@@ -5997,8 +3371,8 @@ new_genop(LoaderState* stp)
     stp->free_genop = p->genop;
 }
 
-static int
-new_label(LoaderState* stp)
+int
+beam_load_new_label(LoaderState* stp)
 {
     unsigned int num = stp->num_labels;
 
@@ -6029,8 +3403,8 @@ new_string_patch(LoaderState* stp, int pos)
     stp->string_patches = p;
 }
 
-static Uint
-new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size)
+Uint
+beam_load_new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size)
 {
     Literal* lit;
 
@@ -6061,8 +3435,8 @@ new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size)
     return stp->num_literals++;
 }
 
-static int
-find_literal(LoaderState* stp, Eterm needle, Uint *idx)
+int
+beam_load_find_literal(LoaderState* stp, Eterm needle, Uint *idx)
 {
     int i;
 
@@ -6655,7 +4029,7 @@ code_module_md5_1(BIF_ALIST_1)
     }
     stp->module = THE_NON_VALUE; /* Suppress diagnostiscs */
     if (!init_iff_file(stp, bytes, binary_size(Bin)) ||
-	!scan_iff_file(stp, chunk_types, NUM_CHUNK_TYPES) ||
+	!scan_iff_file(stp, chunk_types, ERTS_BEAM_NUM_CHUNK_TYPES) ||
 	!verify_chunks(stp)) {
 	res = am_undefined;
 	goto done;
@@ -6995,7 +4369,7 @@ erts_make_stub_module(Process* p, Eterm hipe_magic_bin, Eterm Beam, Eterm Info)
     if (!init_iff_file(stp, bytes, size)) {
 	goto error;
     }
-    if (!scan_iff_file(stp, chunk_types, NUM_CHUNK_TYPES) ||
+    if (!scan_iff_file(stp, chunk_types, ERTS_BEAM_NUM_CHUNK_TYPES) ||
 	!verify_chunks(stp)) {
 	goto error;
     }
@@ -7242,18 +4616,6 @@ int erts_commit_hipe_patch_load(Eterm hipe_magic_bin)
 #endif /* HIPE */
 
 
-static int safe_mul(UWord a, UWord b, UWord* resp)
-{
-    Uint res = a * b; /* XXX:Pan - used in bit syntax, the multiplication has to be stored in Uint */
-    *resp = res;
-
-    if (b == 0) {
-	return 1;
-    } else {
-	return (res / b) == a;
-    }
-}
-
 #ifdef ENABLE_DBG_TRACE_MFA
 
 #define MFA_MAX 10
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index e7127c5b08..eef2a3ad47 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -23,6 +23,10 @@
 
 #include "beam_opcodes.h"
 #include "erl_process.h"
+#include "erl_fun.h"
+
+#define ERTS_BEAM_MAX_OPARGS 8
+#define ERTS_BEAM_NUM_CHUNK_TYPES 11
 
 Eterm beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks,
 			    Eterm module);
@@ -37,6 +41,298 @@ typedef struct gen_op_entry {
 
 extern const GenOpEntry gen_opc[];
 
+/*
+ * Type for an operand for a generic instruction.
+ */
+
+typedef struct {
+    unsigned type;		/* Type of operand. */
+    BeamInstr val;		/* Value of operand. */
+} GenOpArg;
+
+/*
+ * A generic operation.
+ */
+
+typedef struct genop {
+    unsigned int op;		/* Opcode. */
+    int arity;			/* Number of arguments. */
+    GenOpArg def_args[ERTS_BEAM_MAX_OPARGS]; /* Default buffer for arguments. */
+    GenOpArg* a;		/* The arguments. */
+    struct genop* next;		/* Next genop. */
+} GenOp;
+
+/*
+ * The allocation unit for generic blocks.
+ */
+
+typedef struct genop_block {
+    GenOp genop[32];
+    struct genop_block* next;
+} GenOpBlock;
+
+/*
+ * Type for a reference to a label that must be patched.
+ */
+
+typedef struct {
+    Uint pos;                   /* Position of label reference to patch. */
+    Uint offset;                /* Offset from patch location.  */
+    int packed;                 /* 0 (not packed), 1 (lsw), 2 (msw) */
+} LabelPatch;
+
+/*
+ * Type for a label.
+ */
+
+typedef struct {
+    Uint value;			/* Value of label (0 if not known yet). */
+    Uint looprec_targeted;	/* Non-zero if this label is the target of a loop_rec
+				 * instruction.
+				 */
+    LabelPatch* patches;        /* Array of label patches. */
+    Uint num_patches;           /* Number of patches in array. */
+    Uint num_allocated;         /* Number of allocated patches. */
+} Label;
+
+/*
+ * This structure keeps load-time information about a lambda.
+ */
+
+typedef struct {
+    ErlFunEntry* fe;		/* Entry in fun table. */
+    unsigned label;		/* Label of function entry. */
+    Uint32 num_free;		/* Number of free variables. */
+    Eterm function;		/* Name of local function. */
+    int arity;			/* Arity (including free variables). */
+} Lambda;
+
+/*
+ * This structure keeps load-time information about a literal.
+ */
+
+typedef struct {
+    Eterm term;			/* The tagged term (in the heap). */
+    ErlHeapFragment* heap_frags;
+} Literal;
+
+/*
+ * This structure keeps information about an operand that needs to be
+ * patched to contain the correct address of a literal when the code is
+ * frozen.
+ */
+
+typedef struct literal_patch LiteralPatch;
+struct literal_patch {
+    Uint pos;			/* Position in code */
+    LiteralPatch* next;
+};
+
+/*
+ * This structure keeps information about an operand that needs to be
+ * patched to contain the correct address for an address into the string table.
+ */
+
+typedef struct string_patch StringPatch;
+struct string_patch {
+    int pos;			/* Position in code */
+    StringPatch* next;
+};
+
+/*
+ * This structure associates a code offset with a source code location.
+ */
+
+typedef struct {
+    int pos;			/* Position in code */
+    Uint32 loc;			/* Location in source code */
+} LineInstr;
+
+/*
+ * This structure contains information for an imported function or BIF.
+ */
+typedef struct {
+    Eterm module;		/* Tagged atom for module. */
+    Eterm function;		/* Tagged atom for function. */
+    int arity;			/* Arity. */
+    Uint patches;		/* Index to locations in code to
+				 * eventually patch with a pointer into
+				 * the export entry.
+				 */
+    Export *bif;		/* Pointer to export entry if BIF;
+				 * NULL otherwise.
+				 */
+} ImportEntry;
+
+/*
+ * This structure contains information for a function exported from a module.
+ */
+
+typedef struct {
+    Eterm function;		/* Tagged atom for function. */
+    int arity;			/* Arity. */
+    BeamInstr* address;		/* Address to function in code. */
+} ExportEntry;
+
+/*
+ * This structure contains all information about the module being loaded.
+ */
+#define MD5_SIZE 16
+typedef struct LoaderState {
+    /*
+     * The current logical file within the binary.
+     */
+
+    char* file_name;		/* Name of file we are reading (usually chunk name). */
+    byte* file_p;		/* Current pointer within file. */
+    unsigned file_left;		/* Number of bytes left in file. */
+    ErlDrvBinary* bin;		/* Binary holding BEAM file (or NULL) */
+
+    /*
+     * The following are used mainly for diagnostics.
+     */
+
+    Eterm group_leader;		/* Group leader (for diagnostics). */
+    Eterm module;		/* Tagged atom for module name. */
+    Eterm function;		/* Tagged atom for current function
+				 * (or 0 if none).
+				 */
+    unsigned arity;		/* Arity for current function. */
+
+    /*
+     * All found chunks.
+     */
+
+    struct {
+	byte* start;		/* Start of chunk (in binary). */
+	unsigned size;		/* Size of chunk. */
+    } chunks[ERTS_BEAM_NUM_CHUNK_TYPES];
+
+    /*
+     * Used for code loading (mainly).
+     */
+
+    byte* code_start;		/* Start of code file. */
+    unsigned code_size;		/* Size of code file. */
+    int specific_op;		/* Specific opcode (-1 if not found). */
+    unsigned int num_functions; /* Number of functions in module. */
+    unsigned int num_labels;	/* Number of labels. */
+    struct beam_code_header* hdr; /* Loaded code header */
+    BeamInstr* codev;	        /* Loaded code buffer */
+    int        codev_size;      /* Size of code buffer in words. */
+    int ci;			/* Current index into loaded code buffer. */
+    Label* labels;
+    StringPatch* string_patches; /* Linked list of position into string table to patch. */
+    BeamInstr catches;		/* Linked list of catch_yf instructions. */
+    unsigned loaded_size;	/* Final size of code when loaded. */
+    byte mod_md5[MD5_SIZE];	/* MD5 for module code. */
+    int may_load_nif;           /* true if NIFs may later be loaded for this module */
+    int on_load;		/* Index in the code for the on_load function
+				 * (or 0 if there is no on_load function)
+				 */
+    int otp_20_or_higher;       /* Compiled with OTP 20 or higher */
+    unsigned max_opcode;        /* Highest opcode used in module */
+
+    /*
+     * Atom table.
+     */
+
+    unsigned int num_atoms;	/* Number of atoms in atom table. */
+    Eterm* atom;		/* Atom table. */
+
+    unsigned int num_exps;	/* Number of exports. */
+    ExportEntry* export;	/* Pointer to export table. */
+
+    unsigned int num_imports;   /* Number of imports. */
+    ImportEntry* import;	/* Import entry (translated information). */
+
+    /*
+     * Generic instructions.
+     */
+    GenOp* genop;		/* The last generic instruction seen. */
+    GenOp* free_genop;		/* List of free genops. */
+    GenOpBlock* genop_blocks;	/* List of all block of allocated genops. */
+
+    /*
+     * Lambda table.
+     */
+
+    unsigned int num_lambdas;	/* Number of lambdas in table. */
+    unsigned int lambdas_allocated; /* Size of allocated lambda table. */
+    Lambda* lambdas;		/* Pointer to lambdas. */
+    Lambda def_lambdas[16];	/* Default storage for lambda table. */
+    char* lambda_error;		/* Delayed missing 'FunT' error. */
+
+    /*
+     * Literals (constant pool).
+     */
+
+    unsigned int num_literals;	/* Number of literals in table. */
+    unsigned int allocated_literals; /* Number of literal entries allocated. */
+    Literal* literals;		/* Array of literals. */
+    LiteralPatch* literal_patches; /* Operands that need to be patched. */
+    Uint total_literal_size;	/* Total heap size for all literals. */
+
+    /*
+     * Line table.
+     */
+    BeamInstr* line_item;	/* Line items from the BEAM file. */
+    unsigned int num_line_items;/* Number of line items. */
+    LineInstr* line_instr;	/* Line instructions */
+    unsigned int num_line_instrs; /* Maximum number of line instructions */
+    unsigned int current_li;	/* Current line instruction */
+    unsigned int* func_line;	/* Mapping from function to first line instr */
+    Eterm* fname;		/* List of file names */
+    unsigned int num_fnames;	/* Number of filenames in fname table */
+    int loc_size;		/* Size of location info in bytes (2/4) */
+} LoaderState;
+
+#ifdef DEBUG
+# define GARBAGE 0xCC
+# define DEBUG_INIT_GENOP(Dst) sys_memset(Dst, GARBAGE, sizeof(GenOp))
+#else
+# define DEBUG_INIT_GENOP(Dst)
+#endif
+
+#define NEW_GENOP(Stp, Dst)                     \
+  do {                                          \
+    if ((Stp)->free_genop == NULL) {            \
+       beam_load_new_genop((Stp));              \
+    }                                           \
+   Dst = (Stp)->free_genop;                     \
+   (Stp)->free_genop = (Stp)->free_genop->next; \
+   DEBUG_INIT_GENOP(Dst);                       \
+   (Dst)->a = (Dst)->def_args;                  \
+  } while (0)                                   \
+
+#define FREE_GENOP(Stp, Genop)                          \
+ do {                                                   \
+   if ((Genop)->a != (Genop)->def_args) {               \
+       erts_free(ERTS_ALC_T_LOADER_TMP, (Genop)->a);    \
+   }                                                    \
+   (Genop)->next = (Stp)->free_genop;                   \
+   (Stp)->free_genop = (Genop);                         \
+ } while (0)
+
+#define GENOP_NAME_ARITY(Genop, Name, Arity)    \
+  do {                                          \
+    (Genop)->op = genop_##Name##_##Arity;       \
+    (Genop)->arity = Arity;                     \
+  } while (0)
+
+#define GENOP_ARITY(Genop, Arity)                               \
+  do {                                                          \
+   ASSERT((Genop)->a == (Genop)->def_args);                     \
+   (Genop)->arity = (Arity);                                    \
+   (Genop)->a = erts_alloc(ERTS_ALC_T_LOADER_TMP,               \
+                           (Genop)->arity * sizeof(GenOpArg));  \
+  } while (0)
+
+void beam_load_new_genop(LoaderState* stp);
+Uint beam_load_new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size);
+int beam_load_new_label(LoaderState* stp);
+int beam_load_find_literal(LoaderState* stp, Eterm needle, Uint *idx);
+
 struct ErtsLiteralArea_;
 
 /*
@@ -106,6 +402,23 @@ typedef struct beam_code_header {
 
 }BeamCodeHeader;
 
+/*
+ * The transform engine.
+ */
+
+int erts_transform_engine(LoaderState* st);
+
+#define TE_OK 0
+#define TE_FAIL (-1)
+#define TE_SHORT_WINDOW (-2)
+
+int erts_beam_eval_predicate(unsigned int op, LoaderState* st,
+                             GenOpArg var[], GenOpArg* rest_args);
+GenOp* erts_beam_execute_transform(unsigned int op, LoaderState* st,
+                                   GenOpArg var[], GenOpArg* rest_args);
+
+
+
 #  define BEAM_NATIVE_MIN_FUNC_SZ 4
 
 void erts_release_literal_area(struct ErtsLiteralArea_* literal_area);
diff --git a/erts/emulator/beam/beam_transform_engine.c b/erts/emulator/beam/beam_transform_engine.c
new file mode 100644
index 0000000000..d8d800b538
--- /dev/null
+++ b/erts/emulator/beam/beam_transform_engine.c
@@ -0,0 +1,427 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2020. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_vm.h"
+#include "export.h"
+#include "bif.h"
+#include "beam_load.h"
+
+int
+erts_transform_engine(LoaderState* st)
+{
+    Uint op;
+    int ap;			/* Current argument. */
+    const Uint* restart; /* Where to restart if current match fails. */
+    GenOpArg var[TE_MAX_VARS];	/* Buffer for variables. */
+    GenOpArg* rest_args = NULL;
+    int num_rest_args = 0;
+    int i;			/* General index. */
+    Uint mask;
+    GenOp* instr;
+    GenOp* first = st->genop;
+    GenOp* keep = NULL;
+    const Uint* pc;
+    static Uint restart_fail[1] = {TOP_fail};
+
+    ASSERT(gen_opc[first->op].transform != -1);
+    restart = op_transform + gen_opc[first->op].transform;
+
+ restart:
+    ASSERT(restart != NULL);
+    pc = restart;
+    ASSERT(*pc < NUM_TOPS);	/* Valid instruction? */
+    instr = first;
+
+#ifdef DEBUG
+    restart = NULL;
+#endif
+    ap = 0;
+    for (;;) {
+	op = *pc++;
+
+	switch (op) {
+	case TOP_next_instr:
+	    instr = instr->next;
+	    ap = 0;
+	    if (instr == NULL) {
+		/*
+		 * We'll need at least one more instruction to decide whether
+		 * this combination matches or not.
+		 */
+		return TE_SHORT_WINDOW;
+	    }
+	    if (*pc++ != instr->op)
+		goto restart;
+	    break;
+	case TOP_is_type:
+	    mask = *pc++;
+
+	    ASSERT(ap < instr->arity);
+	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+	    if (((1 << instr->a[ap].type) & mask) == 0)
+		goto restart;
+	    break;
+#if defined(TOP_is_type_next_arg)
+	case TOP_is_type_next_arg:
+	    mask = *pc++;
+	    ASSERT(ap < instr->arity);
+	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+	    if (((1 << instr->a[ap].type) & mask) == 0)
+		goto restart;
+            ap++;
+	    break;
+#endif
+	case TOP_pred:
+	    i = *pc++;
+            i = erts_beam_eval_predicate((unsigned) i, st, var, rest_args);
+	    if (i == 0)
+		goto restart;
+	    break;
+#if defined(TOP_is_eq)
+	case TOP_is_eq:
+	    ASSERT(ap < instr->arity);
+	    if (*pc++ != instr->a[ap].val)
+		goto restart;
+	    break;
+#endif
+	case TOP_is_type_eq:
+	    mask = *pc++;
+
+	    ASSERT(ap < instr->arity);
+	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+	    if (((1 << instr->a[ap].type) & mask) == 0)
+		goto restart;
+	    if (*pc++ != instr->a[ap].val)
+		goto restart;
+	    break;
+#if defined(TOP_is_type_eq_next_arg)
+	case TOP_is_type_eq_next_arg:
+	    mask = *pc++;
+            ASSERT(ap < instr->arity);
+            ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+            if (((1 << instr->a[ap].type) & mask) == 0)
+                goto restart;
+            if (*pc++ != instr->a[ap].val)
+                goto restart;
+            ap++;
+            break;
+#endif
+	case TOP_is_same_var:
+	    ASSERT(ap < instr->arity);
+	    i = *pc++;
+	    ASSERT(i < TE_MAX_VARS);
+	    if (var[i].type != instr->a[ap].type)
+		goto restart;
+	    switch (var[i].type) {
+	    case TAG_n:
+		break;
+	    default:
+		if (var[i].val != instr->a[ap].val)
+		    goto restart;
+	    }
+	    break;
+#if defined(TOP_is_bif)
+	case TOP_is_bif:
+	    {
+		int bif_number = *pc++;
+
+		/*
+		 * In debug build, the type must be 'u'.
+		 * In a real build, don't match.  (I.e. retain the original
+		 * call instruction, this will work, but it will be a
+		 * slight performance loss.)
+		 */
+
+		ASSERT(instr->a[ap].type == TAG_u);
+		if (instr->a[ap].type != TAG_u)
+		    goto restart;
+
+		/*
+		 * In debug build, the assertion will catch invalid indexes
+		 * immediately.  In a real build, the loader will issue
+		 * an diagnostic later when the instruction is loaded.
+		 */
+
+		i = instr->a[ap].val;
+		ASSERT(i < st->num_imports);
+		if (i >= st->num_imports || st->import[i].bif == NULL)
+		    goto restart;
+                if (bif_number != -1) {
+                    Export *bif = st->import[i].bif;
+                    if (bif->bif_number != bif_number) {
+                        goto restart;
+                    }
+                }
+	    }
+	    break;
+#endif
+#if defined(TOP_is_not_bif)
+	case TOP_is_not_bif:
+	    {
+		pc++;
+
+		/*
+		 * In debug build, the type must be 'u'.
+		 */
+
+		ASSERT(instr->a[ap].type == TAG_u);
+		if (instr->a[ap].type != TAG_u) {
+		    goto restart;
+		}
+		i = instr->a[ap].val;
+
+		/*
+		 * erlang:apply/2,3 are strange. They exist as (dummy) BIFs
+		 * so that they are included in the export table before
+		 * the erlang module is loaded. They also exist in the erlang
+		 * module as functions. When used in code, a special Beam
+		 * instruction is used.
+		 *
+		 * Below we specially recognize erlang:apply/2,3 as special.
+		 * This is necessary because after setting a trace pattern on
+		 * them, you cannot no longer see from the export entry that
+		 * they are special.
+		 */
+		if (i < st->num_imports) {
+		    if (st->import[i].bif != NULL ||
+			(st->import[i].module == am_erlang &&
+			 st->import[i].function == am_apply &&
+			 (st->import[i].arity == 2 || st->import[i].arity == 3))) {
+			goto restart;
+		    }
+		}
+	    }
+	    break;
+
+#endif
+#if defined(TOP_is_func)
+	case TOP_is_func:
+	    {
+		Eterm mod = *pc++;
+		Eterm func = *pc++;
+		int arity = *pc++;
+
+		ASSERT(instr->a[ap].type == TAG_u);
+		if (instr->a[ap].type != TAG_u) {
+		    goto restart;
+		}
+		i = instr->a[ap].val;
+		ASSERT(i < st->num_imports);
+		if (i >= st->num_imports || st->import[i].module != mod ||
+		    st->import[i].function != func ||
+		    (arity < MAX_ARG && st->import[i].arity != arity)) {
+		    goto restart;
+		}
+	    }
+	    break;
+#endif
+	case TOP_set_var_next_arg:
+	    ASSERT(ap < instr->arity);
+	    i = *pc++;
+	    ASSERT(i < TE_MAX_VARS);
+	    var[i].type = instr->a[ap].type;
+	    var[i].val = instr->a[ap].val;
+	    ap++;
+	    break;
+#if defined(TOP_is_type_set_var_next_arg)
+	case TOP_is_type_set_var_next_arg:
+            mask = pc[0];
+            i = pc[1];
+	    ASSERT(i < TE_MAX_VARS);
+            ASSERT(ap < instr->arity);
+	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+	    if (((1 << instr->a[ap].type) & mask) == 0)
+		goto restart;
+	    ASSERT(i < TE_MAX_VARS);
+	    var[i] = instr->a[ap];
+	    ap++;
+            pc += 2;
+	    break;
+#endif
+#if defined(TOP_is_type_eq_set_var_next_arg)
+	case TOP_is_type_eq_set_var_next_arg:
+            {
+                Eterm val;
+                mask = pc[0];
+                val = pc[1];
+                i = pc[2];
+                ASSERT(i < TE_MAX_VARS);
+                ASSERT(ap < instr->arity);
+                ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+                if (((1 << instr->a[ap].type) & mask) == 0)
+                    goto restart;
+                if (val != instr->a[ap].val)
+                    goto restart;
+                ASSERT(i < TE_MAX_VARS);
+                var[i] = instr->a[ap];
+                ap++;
+                pc += 3;
+            }
+	    break;
+#endif
+#if defined(TOP_rest_args)
+	case TOP_rest_args:
+	    {
+		int formal_arity = gen_opc[instr->op].arity;
+		num_rest_args = instr->arity - formal_arity;
+		rest_args = instr->a + formal_arity;
+	    }
+	    break;
+#endif
+	case TOP_next_arg:
+	    ap++;
+	    break;
+	case TOP_commit:
+	    instr = instr->next; /* The next_instr was optimized away. */
+	    keep = instr;
+	    break;
+#if defined(TOP_commit_new_instr)
+	case TOP_commit_new_instr:
+            /*
+             * Reuse the last instruction on the left side instead of
+             * allocating a new instruction. Note that this is not
+             * safe if TOP_rest_args has been executed; therefore,
+             * this combined instruction is never used when that is
+             * the case.
+             */
+            ASSERT(instr->a == instr->def_args);
+            keep = instr;
+            instr->op = op = *pc++;
+            instr->arity = gen_opc[op].arity;
+            ap = 0;
+            break;
+#endif
+#if defined(TOP_keep)
+	case TOP_keep:
+	    /* Keep the current instruction unchanged. */
+	    keep = instr;
+	    break;
+#endif
+#if defined(TOP_call_end)
+	case TOP_call_end:
+	    {
+		GenOp** lastp;
+		GenOp* new_instr;
+
+		i = *pc++;
+                new_instr = erts_beam_execute_transform((unsigned) i, st, var, rest_args);
+		if (new_instr == NULL) {
+		    goto restart;
+		}
+
+		lastp = &new_instr;
+		while (*lastp != NULL) {
+		    lastp = &((*lastp)->next);
+		}
+
+		keep = instr->next; /* The next_instr was optimized away. */
+		*lastp = keep;
+                instr = new_instr;
+	    }
+	    /* FALLTHROUGH */
+#endif
+	case TOP_end:
+            st->genop = instr;
+	    while (first != keep) {
+		GenOp* next = first->next;
+		FREE_GENOP(st, first);
+		first = next;
+	    }
+	    return TE_OK;
+	case TOP_new_instr:
+	    /*
+	     * Note that the instructions are generated in reverse order.
+	     */
+            {
+                GenOp* new_instr;
+                NEW_GENOP(st, new_instr);
+                new_instr->next = instr;
+                instr = new_instr;
+                instr->op = op = *pc++;
+                instr->arity = gen_opc[op].arity;
+                ap = 0;
+            }
+            break;
+#ifdef TOP_rename
+	case TOP_rename:
+	    instr->op = op = *pc++;
+	    instr->arity = gen_opc[op].arity;
+	    return TE_OK;
+#endif
+	case TOP_store_val_next_arg:
+            instr->a[ap].type = pc[0];
+            instr->a[ap].val = pc[1];
+            ap++;
+            pc += 2;
+            break;
+	case TOP_store_var_next_arg:
+	    i = *pc++;
+	    ASSERT(i < TE_MAX_VARS);
+	    instr->a[ap].type = var[i].type;
+	    instr->a[ap].val = var[i].val;
+	    ap++;
+	    break;
+#if defined(TOP_store_rest_args)
+	case TOP_store_rest_args:
+	    {
+		GENOP_ARITY(instr, instr->arity+num_rest_args);
+		sys_memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg));
+		sys_memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg));
+		ap += num_rest_args;
+	    }
+	    break;
+#endif
+	case TOP_try_me_else:
+	    restart = pc + 1;
+	    restart += *pc++;
+	    ASSERT(*pc < NUM_TOPS); /* Valid instruction? */
+	    break;
+	case TOP_try_me_else_fail:
+	    restart = restart_fail;
+	    break;
+	case TOP_fail:
+	    return TE_FAIL;
+#if defined(TOP_skip_unless)
+	case TOP_skip_unless:
+            /*
+             * Note that the caller of transform_engine() guarantees that
+             * there is always a second instruction available.
+             */
+            ASSERT(instr);
+            if (instr->next->op != pc[0]) {
+                /* The second instruction is wrong. Skip ahead. */
+                pc += pc[1] + 2;
+                ASSERT(*pc < NUM_TOPS); /* Valid instruction? */
+            } else {
+                /* Correct second instruction. */
+                pc += 2;
+            }
+	    break;
+#endif
+	default:
+	    ASSERT(0);
+	}
+    }
+}
diff --git a/erts/emulator/beam/beam_transform_helpers.c b/erts/emulator/beam/beam_transform_helpers.c
new file mode 100644
index 0000000000..546af9aee6
--- /dev/null
+++ b/erts/emulator/beam/beam_transform_helpers.c
@@ -0,0 +1,162 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2020. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "sys.h"
+#include "beam_load.h"
+#include "erl_map.h"
+#include "beam_transform_helpers.h"
+
+typedef struct SortGenOpArg {
+    Eterm term;                 /* Term to use for comparing  */
+    GenOpArg arg;               /* Original data */
+} SortGenOpArg;
+
+static int oparg_compare(GenOpArg* a, GenOpArg* b);
+static int oparg_term_compare(SortGenOpArg* a, SortGenOpArg* b);
+
+int
+beam_load_safe_mul(UWord a, UWord b, UWord* resp)
+{
+    Uint res = a * b;
+    *resp = res;
+
+    if (b == 0) {
+        return 1;
+    } else {
+        return (res / b) == a;
+    }
+}
+
+int
+beam_load_map_key_sort(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
+{
+    SortGenOpArg* t;
+    unsigned size = Size.val;
+    unsigned i;
+
+    if (size == 2) {
+	return 1;		/* Already sorted. */
+    }
+
+
+    t = (SortGenOpArg *) erts_alloc(ERTS_ALC_T_TMP, size*sizeof(SortGenOpArg));
+
+    /*
+     * Copy original data and sort keys to a temporary array.
+     */
+    for (i = 0; i < size; i += 2) {
+	t[i].arg = Rest[i];
+	switch (Rest[i].type) {
+	case TAG_a:
+	    t[i].term = Rest[i].val;
+	    ASSERT(is_atom(t[i].term));
+	    break;
+	case TAG_i:
+	    t[i].term = make_small(Rest[i].val);
+	    break;
+	case TAG_n:
+	    t[i].term = NIL;
+	    break;
+	case TAG_q:
+	    t[i].term = stp->literals[Rest[i].val].term;
+	    break;
+	default:
+	    /*
+	     * Not a literal key. Not allowed. Only a single
+	     * variable key is allowed in each map instruction.
+	     */
+	    erts_free(ERTS_ALC_T_TMP, (void *) t);
+	    return 0;
+	}
+#ifdef DEBUG
+	t[i+1].term = THE_NON_VALUE;
+#endif
+	t[i+1].arg = Rest[i+1];
+    }
+
+    /*
+     * Sort the temporary array.
+     */
+    qsort((void *) t, size / 2, 2 * sizeof(SortGenOpArg),
+	  (int (*)(const void *, const void *)) oparg_term_compare);
+
+    /*
+     * Copy back the sorted, original data.
+     */
+    for (i = 0; i < size; i++) {
+	Rest[i] = t[i].arg;
+    }
+
+    erts_free(ERTS_ALC_T_TMP, (void *) t);
+    return 1;
+}
+
+Eterm
+beam_load_get_literal(LoaderState* stp, GenOpArg Key)
+{
+    switch (Key.type) {
+    case TAG_a:
+        return Key.val;
+    case TAG_i:
+        return make_small(Key.val);
+    case TAG_n:
+        return NIL;
+    case TAG_q:
+        return stp->literals[Key.val].term;
+    default:
+        return THE_NON_VALUE;
+    }
+}
+
+void
+beam_load_sort_select_vals(GenOpArg* base, size_t n)
+{
+    qsort(base, n, 2 * sizeof(GenOpArg),
+          (int (*)(const void *, const void *)) oparg_compare);
+}
+
+static int
+oparg_compare(GenOpArg* a, GenOpArg* b)
+{
+    if (a->val < b->val)
+        return -1;
+    else if (a->val == b->val)
+        return 0;
+    else
+        return 1;
+}
+
+static int
+oparg_term_compare(SortGenOpArg* a, SortGenOpArg* b)
+{
+    Sint res = CMP_TERM(a->term, b->term);
+
+    if (res < 0) {
+        return -1;
+    } else if (res > 0) {
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/erts/emulator/beam/beam_transform_helpers.h b/erts/emulator/beam/beam_transform_helpers.h
new file mode 100644
index 0000000000..af05cb1584
--- /dev/null
+++ b/erts/emulator/beam/beam_transform_helpers.h
@@ -0,0 +1,28 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2020. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifndef __BEAM_TRANSFORM_HELPERS__
+#define __BEAM_TRANSFORM_HELPERS__
+
+int beam_load_safe_mul(UWord a, UWord b, UWord* resp);
+int beam_load_map_key_sort(LoaderState* stp, GenOpArg Size, GenOpArg* Rest);
+Eterm beam_load_get_literal(LoaderState* stp, GenOpArg Key);
+void beam_load_sort_select_vals(GenOpArg* base, size_t n);
+#endif
diff --git a/erts/emulator/beam/float_instrs.tab b/erts/emulator/beam/float_instrs.tab
index 3d4db77892..0bbe451d84 100644
--- a/erts/emulator/beam/float_instrs.tab
+++ b/erts/emulator/beam/float_instrs.tab
@@ -76,13 +76,3 @@ i_fnegate(Src, Dst) {
     $Dst = -$Src;
     ERTS_NO_FPE_ERROR(c_p, $Dst, $BADARITH0());
 }
-
-%unless NO_FPE_SIGNALS
-fclearerror() {
-    ERTS_FP_CHECK_INIT(c_p);
-}
-
-i_fcheckerror() {
-    ERTS_FP_ERROR(c_p, freg[0].fd, $BADARITH0());
-}
-%endif
diff --git a/erts/emulator/beam/generators.tab b/erts/emulator/beam/generators.tab
new file mode 100644
index 0000000000..c3e30d6840
--- /dev/null
+++ b/erts/emulator/beam/generators.tab
@@ -0,0 +1,1270 @@
+// -*- c -*-
+//
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2020. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// %CopyrightEnd%
+//
+
+NewBeamOp(stp, op) {
+    NEW_GENOP($stp, $op);
+    $op->next = NULL;
+}
+
+BeamOpNameArity(op, name, arity) {
+    GENOP_NAME_ARITY($op, $name, $arity);
+}
+
+BeamOpArity(op, arity) {
+    GENOP_ARITY($op, $arity);
+}
+
+NativeEndian(flags) {
+#if defined(WORDS_BIGENDIAN)
+    if (($flags).val & BSF_NATIVE) {
+        ($flags).val &= ~(BSF_LITTLE|BSF_NATIVE);
+    }
+#else
+  if (($flags).val & BSF_NATIVE) {
+      ($flags).val &= ~BSF_NATIVE;
+      ($flags).val |= BSF_LITTLE;
+  }
+#endif
+}
+
+gen.element(Fail, Index, Tuple, Dst) {
+    GenOp* op;
+
+    $NewBeamOp(S, op);
+
+    if (Index.type == TAG_i && Index.val > 0 &&
+        Index.val <= ERTS_MAX_TUPLE_SIZE &&
+        (Tuple.type == TAG_x || Tuple.type == TAG_y)) {
+        $BeamOpNameArity(op, i_fast_element, 4);
+        op->a[0] = Tuple;
+        op->a[1] = Fail;
+        op->a[2].type = TAG_u;
+        op->a[2].val = Index.val;
+        op->a[3] = Dst;
+    } else {
+        $BeamOpNameArity(op, i_element, 4);
+        op->a[0] = Tuple;
+        op->a[1] = Fail;
+        op->a[2] = Index;
+        op->a[3] = Dst;
+    }
+
+    return op;
+}
+
+gen_bs_save_restore(reg, index, instr) {
+    GenOp* op;
+
+    $NewBeamOp(S, op);
+    $BeamOpNameArity(op, $instr, 2);
+    op->a[0] = $reg;
+    op->a[1] = $index;
+    if (Index.type == TAG_u) {
+        op->a[1].val = $index.val+1;
+    } else if ($index.type == TAG_a && $index.val == am_start) {
+        op->a[1].type = TAG_u;
+        op->a[1].val = 0;
+    }
+    return op;
+}
+
+gen.bs_save(Reg, Index) {
+    $gen_bs_save_restore(Reg, Index, i_bs_save2);
+}
+
+gen.bs_restore(Reg, Index) {
+    $gen_bs_save_restore(Reg, Index, i_bs_restore2);
+}
+
+// Generate the fastest instruction to fetch an integer from a binary.
+gen.get_integer2(Fail, Ms, Live, Size, Unit, Flags, Dst) {
+    GenOp* op;
+    UWord bits;
+
+    $NewBeamOp(S, op);
+    $NativeEndian(Flags);
+    if (Size.type == TAG_i) {
+        if (!beam_load_safe_mul(Size.val, Unit.val, &bits)) {
+            goto error;
+        } else if ((Flags.val & BSF_SIGNED) != 0) {
+            goto generic;
+        } else if (bits == 8) {
+            $BeamOpNameArity(op, i_bs_get_integer_8, 3);
+            op->a[0] = Ms;
+            op->a[1] = Fail;
+            op->a[2] = Dst;
+        } else if (bits == 16 && (Flags.val & BSF_LITTLE) == 0) {
+            $BeamOpNameArity(op, i_bs_get_integer_16, 3);
+            op->a[0] = Ms;
+            op->a[1] = Fail;
+            op->a[2] = Dst;
+#ifdef ARCH_64
+        } else if (bits == 32 && (Flags.val & BSF_LITTLE) == 0) {
+            $BeamOpNameArity(op, i_bs_get_integer_32, 3);
+            op->a[0] = Ms;
+            op->a[1] = Fail;
+            op->a[2] = Dst;
+#endif
+        } else {
+        generic:
+            if (bits < SMALL_BITS) {
+                $BeamOpNameArity(op, i_bs_get_integer_small_imm, 5);
+                op->a[0] = Ms;
+                op->a[1].type = TAG_u;
+                op->a[1].val = bits;
+                op->a[2] = Fail;
+                op->a[3] = Flags;
+                op->a[4] = Dst;
+            } else {
+                $BeamOpNameArity(op, i_bs_get_integer_imm, 6);
+                op->a[0] = Ms;
+                op->a[1].type = TAG_u;
+                op->a[1].val = bits;
+                op->a[2] = Live;
+                op->a[3] = Fail;
+                op->a[4] = Flags;
+                op->a[5] = Dst;
+            }
+        }
+    } else if (Size.type == TAG_q) {
+        Eterm big = S->literals[Size.val].term;
+        Uint bigval;
+
+        if (!term_to_Uint(big, &bigval)) {
+        error:
+            $BeamOpNameArity(op, jump, 1);
+            op->a[0] = Fail;
+        } else {
+            if (!beam_load_safe_mul(bigval, Unit.val, &bits)) {
+                goto error;
+            }
+            goto generic;
+        }
+    } else if (Size.type == TAG_x || Size.type == TAG_y) {
+        $BeamOpNameArity(op, i_bs_get_integer, 6);
+        op->a[0] = Ms;
+        op->a[1] = Fail;
+        op->a[2] = Live;
+        op->a[3].type = TAG_u;
+        op->a[3].val = (Unit.val << 3) | Flags.val;
+        op->a[4] = Size;
+        op->a[5] = Dst;
+        return op;
+    } else {
+        /* Invalid literal size. */
+        goto error;
+    }
+    return op;
+}
+
+// Generate the fastest instruction to fetch a binary from a binary.
+gen.get_binary2(Fail, Ms, Live, Size, Unit, Flags, Dst) {
+    GenOp* op;
+    $NewBeamOp(S, op);
+
+    $NativeEndian(Flags);
+    if (Size.type == TAG_a && Size.val == am_all) {
+        $BeamOpNameArity(op, i_bs_get_binary_all2, 5);
+        op->a[0] = Ms;
+        op->a[1] = Fail;
+        op->a[2] = Live;
+        op->a[3] = Unit;
+        op->a[4] = Dst;
+    } else if (Size.type == TAG_i) {
+        $BeamOpNameArity(op, i_bs_get_binary_imm2, 6);
+        op->a[0] = Ms;
+        op->a[1] = Fail;
+        op->a[2] = Live;
+        op->a[3].type = TAG_u;
+        if (!beam_load_safe_mul(Size.val, Unit.val, &op->a[3].val)) {
+            goto error;
+        }
+        op->a[4] = Flags;
+        op->a[5] = Dst;
+    } else if (Size.type == TAG_q) {
+        Eterm big = S->literals[Size.val].term;
+        Uint bigval;
+
+        if (!term_to_Uint(big, &bigval)) {
+        error:
+            $BeamOpNameArity(op, jump, 1);
+            op->a[0] = Fail;
+        } else {
+            $BeamOpNameArity(op, i_bs_get_binary_imm2, 6);
+            op->a[0] = Ms;
+            op->a[1] = Fail;
+            op->a[2] = Live;
+            op->a[3].type = TAG_u;
+            if (!beam_load_safe_mul(bigval, Unit.val, &op->a[3].val)) {
+                goto error;
+            }
+            op->a[4] = Flags;
+            op->a[5] = Dst;
+        }
+    } else if (Size.type == TAG_x || Size.type == TAG_y) {
+        $BeamOpNameArity(op, i_bs_get_binary2, 6);
+        op->a[0] = Ms;
+        op->a[1] = Fail;
+        op->a[2] = Live;
+        op->a[3] = Size;
+        op->a[4].type = TAG_u;
+        op->a[4].val = (Unit.val << 3) | Flags.val;
+        op->a[5] = Dst;
+    } else {
+        /* Invalid literal size. */
+        goto error;
+    }
+    return op;
+}
+
+// Generate an instruction to fetch a float from a binary.
+gen.get_float2(Fail, Ms, Live, Size, Unit, Flags, Dst) {
+    GenOp* op;
+    $NewBeamOp(S, op);
+
+    $NativeEndian(Flags);
+    $BeamOpNameArity(op, i_bs_get_float2, 6);
+    op->a[0] = Ms;
+    op->a[1] = Fail;
+    op->a[2] = Live;
+    op->a[3] = Size;
+    op->a[4].type = TAG_u;
+    op->a[4].val = (Unit.val << 3) | Flags.val;
+    op->a[5] = Dst;
+    return op;
+}
+
+gen.put_binary(Fail, Size, Unit, Flags, Src) {
+    GenOp* op;
+    $NewBeamOp(S, op);
+
+    $NativeEndian(Flags);
+    if (Size.type == TAG_a && Size.val == am_all) {
+        $BeamOpNameArity(op, i_new_bs_put_binary_all, 3);
+        op->a[0] = Src;
+        op->a[1] = Fail;
+        op->a[2] = Unit;
+    } else if (Size.type == TAG_i) {
+        $BeamOpNameArity(op, i_new_bs_put_binary_imm, 3);
+        op->a[0] = Fail;
+        op->a[1].type = TAG_u;
+        if (beam_load_safe_mul(Size.val, Unit.val, &op->a[1].val)) {
+            op->a[2] = Src;
+        } else {
+        error:
+            $BeamOpNameArity(op, badarg, 1);
+            op->a[0] = Fail;
+        }
+    } else if (Size.type == TAG_q) {
+#ifdef ARCH_64
+        /*
+         * There is no way that this binary would fit in memory.
+         */
+        goto error;
+#else
+        Eterm big = S->literals[Size.val].term;
+        Uint bigval;
+        Uint size;
+
+        if (!term_to_Uint(big, &bigval) ||
+            !beam_load_safe_mul(bigval, Unit.val, &size)) {
+            goto error;
+        }
+        $BeamOpNameArity(op, i_new_bs_put_binary_imm, 3);
+        op->a[0] = Fail;
+        op->a[1].type = TAG_u;
+        op->a[1].val = size;
+        op->a[2] = Src;
+#endif
+    } else {
+        $BeamOpNameArity(op, i_new_bs_put_binary, 4);
+        op->a[0] = Fail;
+        op->a[1] = Size;
+        op->a[2].type = TAG_u;
+        op->a[2].val = (Unit.val << 3) | (Flags.val & 7);
+        op->a[3] = Src;
+    }
+
+    return op;
+}
+
+gen.put_integer(Fail, Size, Unit, Flags, Src) {
+    GenOp* op;
+    $NewBeamOp(S, op);
+
+    $NativeEndian(Flags);
+    /* Negative size must fail */
+    if (Size.type == TAG_i) {
+        Uint size;
+        if (!beam_load_safe_mul(Size.val, Unit.val, &size)) {
+        error:
+            $BeamOpNameArity(op, badarg, 1);
+            op->a[0] = Fail;
+            return op;
+        }
+        $BeamOpNameArity(op, i_new_bs_put_integer_imm, 4);
+        op->a[0] = Src;
+        op->a[1] = Fail;
+        op->a[2].type = TAG_u;
+        op->a[2].val = size;
+        op->a[3].type = Flags.type;
+        op->a[3].val = (Flags.val & 7);
+    } else if (Size.type == TAG_q) {
+        Eterm big = S->literals[Size.val].term;
+        Uint bigval;
+        Uint size;
+
+        if (!term_to_Uint(big, &bigval) ||
+            !beam_load_safe_mul(bigval, Unit.val, &size)) {
+            goto error;
+        }
+        $BeamOpNameArity(op, i_new_bs_put_integer_imm, 4);
+        op->a[0] = Src;
+        op->a[1] = Fail;
+        op->a[2].type = TAG_u;
+        op->a[2].val = size;
+        op->a[3].type = Flags.type;
+        op->a[3].val = (Flags.val & 7);
+    } else {
+        $BeamOpNameArity(op, i_new_bs_put_integer, 4);
+        op->a[0] = Fail;
+        op->a[1] = Size;
+        op->a[2].type = TAG_u;
+        op->a[2].val = (Unit.val << 3) | (Flags.val & 7);
+        op->a[3] = Src;
+    }
+    return op;
+}
+
+gen.put_float(Fail, Size, Unit, Flags, Src) {
+    GenOp* op;
+    $NewBeamOp(S, op);
+
+    $NativeEndian(Flags);
+    if (Size.type == TAG_i) {
+        $BeamOpNameArity(op, i_new_bs_put_float_imm, 4);
+        op->a[0] = Fail;
+        op->a[1].type = TAG_u;
+        if (!beam_load_safe_mul(Size.val, Unit.val, &op->a[1].val)) {
+            $BeamOpNameArity(op, badarg, 1);
+            op->a[0] = Fail;
+        } else {
+            op->a[2] = Flags;
+            op->a[3] = Src;
+        }
+    } else {
+        $BeamOpNameArity(op, i_new_bs_put_float, 4);
+        op->a[0] = Fail;
+        op->a[1] = Size;
+        op->a[2].type = TAG_u;
+        op->a[2].val = (Unit.val << 3) | (Flags.val & 7);
+        op->a[3] = Src;
+    }
+    return op;
+}
+
+// Generate the fastest instruction for bs_skip_bits.
+gen.skip_bits2(Fail, Ms, Size, Unit, Flags) {
+    GenOp* op;
+    $NewBeamOp(S, op);
+
+    $NativeEndian(Flags);
+    if (Size.type == TAG_a && Size.val == am_all) {
+        /*
+         * This kind of skip instruction will only be found in modules
+         * compiled before OTP 19. From OTP 19, the compiler generates
+         * a test_unit instruction of a bs_skip at the end of a
+         * binary.
+         *
+         * It is safe to replace the skip instruction with a test_unit
+         * instruction, because the position will never be used again.
+         * If the match context itself is used again, it will be used by
+         * a bs_restore2 instruction which will overwrite the position
+         * by one of the stored positions.
+         */
+        $BeamOpNameArity(op, bs_test_unit, 3);
+        op->a[0] = Fail;
+        op->a[1] = Ms;
+        op->a[2] = Unit;
+    } else if (Size.type == TAG_i) {
+        $BeamOpNameArity(op, i_bs_skip_bits_imm2, 3);
+        op->a[0] = Fail;
+        op->a[1] = Ms;
+        op->a[2].type = TAG_u;
+        if (!beam_load_safe_mul(Size.val, Unit.val, &op->a[2].val)) {
+            goto error;
+        }
+    } else if (Size.type == TAG_q) {
+        Eterm big = S->literals[Size.val].term;
+        Uint bigval;
+
+        if (!term_to_Uint(big, &bigval)) {
+        error:
+            $BeamOpNameArity(op, jump, 1);
+            op->a[0] = Fail;
+        } else {
+            $BeamOpNameArity(op, i_bs_skip_bits_imm2, 3);
+            op->a[0] = Fail;
+            op->a[1] = Ms;
+            op->a[2].type = TAG_u;
+            if (!beam_load_safe_mul(bigval, Unit.val, &op->a[2].val)) {
+                goto error;
+            }
+        }
+    } else if (Size.type == TAG_x || Size.type == TAG_y) {
+        $BeamOpNameArity(op, i_bs_skip_bits2, 4);
+        op->a[0] = Ms;
+        op->a[1] = Size;
+        op->a[2] = Fail;
+        op->a[3] = Unit;
+    } else {
+        /*
+         * Invalid literal size. Can only happen if compiler
+         * optimizations are selectively disabled.  For example,
+         * at the time of writing, [no_copt, no_type_opt] will allow
+         * skip instructions with invalid sizes to slip through.
+         */
+        goto error;
+    }
+    return op;
+}
+
+gen_increment(stp, reg, val, dst) {
+    GenOp* op;
+    $NewBeamOp($stp, op);
+    $BeamOpNameArity(op, i_increment, 3);
+    op->a[0] = $reg;
+    op->a[1].type = TAG_u;
+    op->a[1].val = $val;
+    op->a[2] = $dst;
+    return op;
+}
+
+gen.increment(Reg, Integer, Dst) {
+    $gen_increment(S, Reg, Integer.val, Dst);
+}
+
+gen.increment_from_minus(Reg, Integer, Dst) {
+    $gen_increment(S, Reg, -Integer.val, Dst);
+}
+
+gen.plus_from_minus(Fail, Live, Src, Integer, Dst) {
+    GenOp* op;
+
+    $NewBeamOp(S, op);
+    $BeamOpNameArity(op, gen_plus, 5);
+    op->a[0] = Fail;
+    op->a[1] = Live;
+    op->a[2] = Src;
+    op->a[3].type = TAG_i;
+    op->a[3].val = -Integer.val;
+    op->a[4] = Dst;
+    return op;
+}
+
+// Macro for generating a timeout instruction for a literal timeout value.
+gen_literal_timeout(stp, fail, time, succ_instr, fail_instr) {
+    GenOp* op;
+    Sint timeout;
+
+    $NewBeamOp($stp, op);
+    $BeamOpNameArity(op, $succ_instr, 2);
+    op->a[0].type = TAG_u;
+    op->a[1] = $fail;
+
+    if ($time.type == TAG_i && (timeout = $time.val) >= 0 &&
+#if defined(ARCH_64)
+        (timeout >> 32) == 0
+#else
+        1
+#endif
+        ) {
+        op->a[0].val = timeout;
+#if !defined(ARCH_64)
+    } else if ($time.type == TAG_q) {
+        Eterm big;
+
+        big = $stp->literals[$time.val].term;
+        if (is_not_big(big)) {
+            goto error;
+        }
+        if (big_arity(big) > 1 || big_sign(big)) {
+            goto error;
+        } else {
+            Uint u;
+            (void) term_to_Uint(big, &u);
+            op->a[0].val = (BeamInstr) u;
+        }
+#endif
+    } else {
+#if !defined(ARCH_64)
+    error:
+#endif
+        $BeamOpNameArity(op, $fail_instr, 0);
+    }
+    return op;
+}
+
+gen.literal_timeout(Fail, Time) {
+    $gen_literal_timeout(S, Fail, Time, wait_timeout_unlocked_int, i_wait_error);
+}
+
+gen.literal_timeout_locked(Fail, Time) {
+    $gen_literal_timeout(S, Fail, Time, wait_timeout_locked_int, i_wait_error_locked);
+}
+
+// Tag the list of values with tuple arity tags.
+gen.select_tuple_arity(Src, Fail, Size, Rest) {
+    GenOp* op;
+    GenOpArg *tmp;
+    int arity = Size.val + 3;
+    int size = Size.val / 2;
+    int i, j, align = 0;
+
+    /*
+     * Verify the validity of the list.
+     */
+
+    if (Size.val % 2 != 0) {
+        return NULL;
+    }
+    for (i = 0; i < Size.val; i += 2) {
+        if (Rest[i].type != TAG_u || Rest[i+1].type != TAG_f) {
+            return NULL;
+        }
+    }
+
+    /*
+     * Use a special-cased instruction if there are only two values.
+     */
+    if (size == 2) {
+        $NewBeamOp(S, op);
+        $BeamOpNameArity(op, i_select_tuple_arity2, 4);
+        $BeamOpArity(op, arity - 1);
+        op->a[0] = Src;
+        op->a[1] = Fail;
+        op->a[2].type = TAG_u;
+        op->a[2].val  = Rest[0].val;
+        op->a[3].type = TAG_u;
+        op->a[3].val  = Rest[2].val;
+        op->a[4] = Rest[1];
+        op->a[5] = Rest[3];
+
+        return op;
+    }
+
+    /*
+     * Generate the generic instruction.
+     * Assumption:
+     *   Few different tuple arities to select on (fewer than 20).
+     *   Use linear scan approach.
+     */
+
+    align = 1;
+
+    arity += 2*align;
+    size  += align;
+
+    $NewBeamOp(S, op);
+    $BeamOpNameArity(op, i_select_tuple_arity, 3);
+    $BeamOpArity(op, arity);
+    op->a[0] = Src;
+    op->a[1] = Fail;
+    op->a[2].type = TAG_u;
+    op->a[2].val = size;
+
+    tmp = (GenOpArg *) erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(GenOpArg)*(arity-2*align));
+
+    for (i = 3; i < arity - 2*align; i+=2) {
+        tmp[i-3].type = TAG_v;
+        tmp[i-3].val  = make_arityval(Rest[i-3].val);
+        tmp[i-2]      = Rest[i-2];
+    }
+
+    /*
+     * Sort the values to make them useful for a sentinel search.
+     */
+
+    beam_load_sort_select_vals(tmp, size - align);
+
+    j = 3;
+    for (i = 3; i < arity - 2*align; i += 2) {
+        op->a[j]        = tmp[i-3];
+        op->a[j + size] = tmp[i-2];
+        j++;
+    }
+
+    erts_free(ERTS_ALC_T_LOADER_TMP, (void *) tmp);
+
+    op->a[j].type = TAG_u;
+    op->a[j].val  = ~((BeamInstr)0);
+    op->a[j+size] = Fail;
+
+    return op;
+}
+
+// Split a list consisting of both small and bignums into two
+// select_val instructions.
+gen.split_values(Src, TypeFail, Fail, Size, Rest) {
+    GenOp* op1;
+    GenOp* op2;
+    GenOp* label;
+    GenOp* is_integer;
+    int i;
+
+    ASSERT(Size.val >= 2 && Size.val % 2 == 0);
+
+    $NewBeamOp(S,  is_integer);
+    $BeamOpNameArity(is_integer, is_integer, 2);
+    is_integer->a[0] = TypeFail;
+    is_integer->a[1] = Src;
+
+    $NewBeamOp(S,  label);
+    $BeamOpNameArity(label, label, 1);
+    label->a[0].type = TAG_u;
+    label->a[0].val = beam_load_new_label(S);
+
+    $NewBeamOp(S,  op1);
+    $BeamOpNameArity(op1, select_val, 3);
+    $BeamOpArity(op1, 3 + Size.val);
+    op1->a[0] = Src;
+    op1->a[1].type = TAG_f;
+    op1->a[1].val = label->a[0].val;
+    op1->a[2].type = TAG_u;
+    op1->a[2].val = 0;
+
+    $NewBeamOp(S,  op2);
+    $BeamOpNameArity(op2, select_val, 3);
+    $BeamOpArity(op2, 3 + Size.val);
+    op2->a[0] = Src;
+    op2->a[1] = Fail;
+    op2->a[2].type = TAG_u;
+    op2->a[2].val = 0;
+
+    /*
+     * Split the list.
+     */
+
+    ASSERT(Size.type == TAG_u);
+    for (i = 0; i < Size.val; i += 2) {
+        GenOp* op = (Rest[i].type == TAG_q) ? op2 : op1;
+        int dst = 3 + op->a[2].val;
+
+        ASSERT(Rest[i+1].type == TAG_f);
+        op->a[dst] = Rest[i];
+        op->a[dst+1] = Rest[i+1];
+        op->arity += 2;
+        op->a[2].val += 2;
+    }
+    ASSERT(op1->a[2].val > 0);
+    ASSERT(op2->a[2].val > 0);
+
+    /*
+     * Order the instruction sequence appropriately.
+     */
+
+    if (TypeFail.val == Fail.val) {
+        /*
+         * select_val L1 S ... (small numbers)
+         * label L1
+         * is_integer Fail S
+         * select_val Fail S ... (bignums)
+         */
+        op1->next = label;
+        label->next = is_integer;
+        is_integer->next = op2;
+    } else {
+        /*
+         * is_integer TypeFail S
+         * select_val L1 S ... (small numbers)
+         * label L1
+         * select_val Fail S ... (bignums)
+         */
+        is_integer->next = op1;
+        op1->next = label;
+        label->next = op2;
+        op1 = is_integer;
+    }
+
+    return op1;
+}
+
+// Generate a jump table.
+gen.jump_tab(Src, Fail, Size, Rest) {
+    Sint min, max;
+    Sint i;
+    Sint size;
+    Sint arity;
+    int fixed_args;
+    GenOp* op;
+
+    ASSERT(Size.val >= 2 && Size.val % 2 == 0);
+
+    /*
+     * If there is only one choice, don't generate a jump table.
+     */
+    if (Size.val == 2) {
+        GenOp* jump;
+
+        $NewBeamOp(S, op);
+        $BeamOpNameArity(op, is_ne_exact, 3);
+        op->a[0] = Rest[1];
+        op->a[1] = Src;
+        op->a[2] = Rest[0];
+
+        $NewBeamOp(S,  jump);
+        $BeamOpNameArity(jump, jump, 1);
+        jump->a[0] = Fail;
+
+        op->next = jump;
+        return op;
+    }
+
+    /*
+     * Calculate the minimum and maximum values and size of jump table.
+     */
+
+    ASSERT(Rest[0].type == TAG_i);
+    min = max = Rest[0].val;
+    for (i = 2; i < Size.val; i += 2) {
+        ASSERT(Rest[i].type == TAG_i && Rest[i+1].type == TAG_f);
+        if (Rest[i].val < min) {
+            min = Rest[i].val;
+        } else if (max < Rest[i].val) {
+            max = Rest[i].val;
+        }
+    }
+    size = max - min + 1;
+
+    /*
+     * Allocate structure and fill in the fixed fields.
+     */
+
+    $NewBeamOp(S, op);
+    if (min == 0) {
+        $BeamOpNameArity(op, i_jump_on_val_zero, 3);
+    } else {
+        $BeamOpNameArity(op, i_jump_on_val, 4);
+    }
+    fixed_args = op->arity;
+    arity = fixed_args + size;
+    $BeamOpArity(op, arity);
+    op->a[0] = Src;
+    op->a[1] = Fail;
+    op->a[2].type = TAG_u;
+    op->a[2].val = size;
+    op->a[3].type = TAG_u;
+    op->a[3].val = min;
+
+
+    /*
+     * Fill in the jump table.
+     */
+
+    for (i = fixed_args; i < arity; i++) {
+        op->a[i] = Fail;
+    }
+    for (i = 0; i < Size.val; i += 2) {
+        Sint index;
+        index = fixed_args+Rest[i].val-min;
+        ASSERT(fixed_args <= index && index < arity);
+        op->a[index] = Rest[i+1];
+    }
+    return op;
+}
+
+
+// Generate a select_val instruction.  We know that a jump table is
+// not suitable, and that all values are of the same type (integer or
+// atoms).
+gen.select_val(Src, Fail, Size, Rest) {
+    GenOp* op;
+    GenOpArg *tmp;
+    int arity = Size.val + 3;
+    int size = Size.val / 2;
+    int i, j, align = 0;
+
+    if (size == 2) {
+        /*
+         * Use a special-cased instruction if there are only two values.
+         */
+
+        $NewBeamOp(S, op);
+        $BeamOpNameArity(op, i_select_val2, 4);
+        $BeamOpArity(op, arity - 1);
+        op->a[0] = Src;
+        op->a[1] = Fail;
+        op->a[2] = Rest[0];
+        op->a[3] = Rest[2];
+        op->a[4] = Rest[1];
+        op->a[5] = Rest[3];
+
+        return op;
+    }
+
+    if (size <= 10) {
+        /* Use linear search. Reserve place for a sentinel. */
+        align = 1;
+    }
+
+    arity += 2*align;
+    size  += align;
+
+    $NewBeamOp(S, op);
+    if (align == 0) {
+        $BeamOpNameArity(op, i_select_val_bins, 3);
+    } else {
+        $BeamOpNameArity(op, i_select_val_lins, 3);
+    }
+    $BeamOpArity(op, arity);
+    op->a[0] = Src;
+    op->a[1] = Fail;
+    op->a[2].type = TAG_u;
+    op->a[2].val = size;
+
+    tmp = (GenOpArg *) erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(GenOpArg)*(arity-2*align));
+
+    for (i = 3; i < arity - 2*align; i++) {
+        tmp[i-3] = Rest[i-3];
+    }
+
+    /*
+     * Sort the values to make them useful for a binary or sentinel search.
+     */
+
+    beam_load_sort_select_vals(tmp, size - align);
+
+    j = 3;
+    for (i = 3; i < arity - 2*align; i += 2) {
+        op->a[j]      = tmp[i-3];
+        op->a[j+size] = tmp[i-2];
+        j++;
+    }
+
+    erts_free(ERTS_ALC_T_LOADER_TMP, (void *) tmp);
+
+    if (align) {
+        /* Add sentinel for linear search. */
+        op->a[j].type = TAG_u;
+        op->a[j].val  = ~((BeamInstr)0);
+        op->a[j+size] = Fail;
+    }
+
+#ifdef DEBUG
+    for (i = 0; i < size - 1; i++) {
+        ASSERT(op->a[i+3].val <= op->a[i+4].val);
+    }
+#endif
+
+    return op;
+}
+
+// Generate a select_val instruction for big numbers.
+gen.select_literals(Src, Fail, Size, Rest) {
+    GenOp* op;
+    GenOp* jump;
+    GenOp** prev_next = &op;
+
+    int i;
+
+    for (i = 0; i < Size.val; i += 2) {
+        GenOp* op;
+        ASSERT(Rest[i].type == TAG_q);
+
+        $NewBeamOp(S, op);
+        $BeamOpNameArity(op, is_ne_exact, 3);
+        op->a[0] = Rest[i+1];
+        op->a[1] = Src;
+        op->a[2] = Rest[i];
+        *prev_next = op;
+        prev_next = &op->next;
+    }
+
+    $NewBeamOp(S, jump);
+    $BeamOpNameArity(jump, jump, 1);
+    jump->a[0] = Fail;
+    *prev_next = jump;
+    return op;
+}
+
+
+
+// Replace a select_val instruction with a constant controlling
+// expression with a jump instruction.
+gen.const_select_val(Src, Fail, Size, Rest) {
+    GenOp* op;
+    int i;
+
+    ASSERT(Size.type == TAG_u);
+
+    $NewBeamOp(S, op);
+    $BeamOpNameArity(op, jump, 1);
+
+    /*
+     * Search for a literal matching the controlling expression.
+     */
+
+    switch (Src.type) {
+    case TAG_q:
+        {
+            Eterm expr = S->literals[Src.val].term;
+            for (i = 0; i < Size.val; i += 2) {
+                if (Rest[i].type == TAG_q) {
+                    Eterm term = S->literals[Rest[i].val].term;
+                    if (eq(term, expr)) {
+                        ASSERT(Rest[i+1].type == TAG_f);
+                        op->a[0] = Rest[i+1];
+                        return op;
+                    }
+                }
+            }
+        }
+        break;
+    case TAG_i:
+    case TAG_a:
+        for (i = 0; i < Size.val; i += 2) {
+            if (Rest[i].val == Src.val && Rest[i].type == Src.type) {
+                ASSERT(Rest[i+1].type == TAG_f);
+                op->a[0] = Rest[i+1];
+                return op;
+            }
+        }
+        break;
+    }
+
+    /*
+     * No match.  Use the failure label.
+     */
+
+    op->a[0] = Fail;
+    return op;
+}
+
+gen.make_fun2(idx) {
+    ErlFunEntry* fe;
+    GenOp* op;
+    Uint arity, num_free;
+
+    if (idx.val >= S->num_lambdas) {
+        S->lambda_error = "missing or short chunk 'FunT'";
+        fe = 0;
+        num_free = 0;
+        arity = 0;
+    } else {
+        fe = S->lambdas[idx.val].fe;
+        num_free = S->lambdas[idx.val].num_free;
+        arity = fe->arity;
+    }
+
+    $NewBeamOp(S, op);
+
+    /*
+     * It's possible this is called before init process is started,
+     * skip the optimisation in such case.
+     */
+    if (num_free == 0 && erts_init_process_id != ERTS_INVALID_PID) {
+        Uint lit;
+        Eterm* hp;
+        ErlFunThing* funp;
+
+        lit = beam_load_new_literal(S, &hp, ERL_FUN_SIZE);
+        funp = (ErlFunThing *) hp;
+        erts_refc_inc(&fe->refc, 2);
+        funp->thing_word = HEADER_FUN;
+        funp->next = NULL;
+        funp->fe = fe;
+        funp->num_free = 0;
+        funp->creator = erts_init_process_id;
+        funp->arity = arity;
+
+        /*
+         * Use a move_fun/2 instruction to load the fun to enable
+         * further optimizations.
+         */
+        $BeamOpNameArity(op, move_fun, 2);
+        op->a[0].type = TAG_q;
+        op->a[0].val = lit;
+        op->a[1].type = TAG_x;
+        op->a[1].val = 0;
+    } else {
+        $BeamOpNameArity(op, i_make_fun, 2);
+        op->a[0].type = TAG_u;
+        op->a[0].val = (BeamInstr) fe;
+        op->a[1].type = TAG_u;
+        op->a[1].val = num_free;
+    }
+
+    return op;
+}
+
+gen.is_function2(Fail, Fun, Arity) {
+    GenOp* op;
+    int literal_arity =  Arity.type == TAG_i;
+    int fun_is_reg = Fun.type == TAG_x || Fun.type == TAG_y;
+
+    $NewBeamOp(S, op);
+
+    if (fun_is_reg &&literal_arity) {
+        /*
+         * Most common case. Fun in a register and arity
+         * is an integer literal.
+         */
+        if (Arity.val > MAX_ARG) {
+            /* Arity is negative or too big. */
+            $BeamOpNameArity(op, jump, 1);
+            op->a[0] = Fail;
+            return op;
+        } else {
+            $BeamOpNameArity(op, hot_is_function2, 3);
+            op->a[0] = Fail;
+            op->a[1] = Fun;
+            op->a[2].type = TAG_u;
+            op->a[2].val = Arity.val;
+            return op;
+        }
+    } else {
+        /*
+         * Handle extremely uncommon cases by a slower sequence.
+         */
+        GenOp* move_fun;
+        GenOp* move_arity;
+
+        $NewBeamOp(S, move_fun);
+        $NewBeamOp(S, move_arity);
+
+        move_fun->next = move_arity;
+        move_arity->next = op;
+
+        $BeamOpNameArity(move_fun, move, 2);
+        move_fun->a[0] = Fun;
+        move_fun->a[1].type = TAG_x;
+        move_fun->a[1].val = 1022;
+
+        $BeamOpNameArity(move_arity, move, 2);
+        move_arity->a[0] = Arity;
+        move_arity->a[1].type = TAG_x;
+        move_arity->a[1].val = 1023;
+
+        $BeamOpNameArity(op, cold_is_function2, 3);
+        op->a[0] = Fail;
+        op->a[1].type = TAG_x;
+        op->a[1].val = 1022;
+        op->a[2].type = TAG_x;
+        op->a[2].val = 1023;
+        return move_fun;
+    }
+}
+
+gen.tuple_append_put5(Arity, Dst, Puts, S1, S2, S3, S4, S5) {
+    GenOp* op;
+    int arity = Arity.val;      /* Arity of tuple, not the instruction */
+    int i;
+
+    $NewBeamOp(S, op);
+    $BeamOpNameArity(op, i_put_tuple, 2);
+    $BeamOpArity(op, arity+2+5);
+    op->a[0] = Dst;
+    op->a[1].type = TAG_u;
+    op->a[1].val = arity + 5;
+    for (i = 0; i < arity; i++) {
+        op->a[i+2] = Puts[i];
+    }
+    op->a[arity+2] = S1;
+    op->a[arity+3] = S2;
+    op->a[arity+4] = S3;
+    op->a[arity+5] = S4;
+    op->a[arity+6] = S5;
+    return op;
+}
+
+gen.tuple_append_put(Arity, Dst, Puts, Src) {
+    GenOp* op;
+    int arity = Arity.val;      /* Arity of tuple, not the instruction */
+    int i;
+
+    $NewBeamOp(S, op);
+    $BeamOpNameArity(op, i_put_tuple, 2);
+    $BeamOpArity(op, arity+2+1);
+    op->a[0] = Dst;
+    op->a[1].type = TAG_u;
+    op->a[1].val = arity + 1;
+    for (i = 0; i < arity; i++) {
+        op->a[i+2] = Puts[i];
+    }
+    op->a[arity+2] = Src;
+    return op;
+}
+
+// Generate an instruction for get/1.
+gen.get(Src, Dst) {
+    GenOp* op;
+    Eterm key_term;
+
+    $NewBeamOp(S, op);
+    key_term = beam_load_get_literal(S, Src);
+    if (is_value(key_term)) {
+        $BeamOpNameArity(op, i_get_hash, 3);
+        op->a[0] = Src;
+        op->a[1].type = TAG_u;
+        op->a[1].val = (BeamInstr) erts_pd_make_hx(key_term);
+        op->a[2] = Dst;
+    } else {
+        $BeamOpNameArity(op, i_get, 2);
+        op->a[0] = Src;
+        op->a[1] = Dst;
+    }
+    return op;
+}
+
+gen.new_small_map_lit(Dst, Live, Size, Rest) {
+    unsigned size = Size.val;
+    Uint lit;
+    unsigned i;
+    GenOp* op;
+    GenOpArg* dst;
+    Eterm* hp;
+    Eterm* tmp;
+    Eterm* thp;
+    Eterm keys;
+
+    $NewBeamOp(S, op);
+    $BeamOpNameArity(op, i_new_small_map_lit, 3);
+    $BeamOpArity(op, 3 + size/2);
+
+    tmp = thp = erts_alloc(ERTS_ALC_T_LOADER_TMP, (1 + size/2) * sizeof(*tmp));
+    keys = make_tuple(thp);
+    *thp++ = make_arityval(size/2);
+
+    dst = op->a+3;
+
+    for (i = 0; i < size; i += 2) {
+        switch (Rest[i].type) {
+        case TAG_a:
+            *thp++ = Rest[i].val;
+            ASSERT(is_atom(Rest[i].val));
+            break;
+        case TAG_i:
+            *thp++ = make_small(Rest[i].val);
+            break;
+        case TAG_n:
+            *thp++ = NIL;
+            break;
+        case TAG_q:
+            *thp++ = S->literals[Rest[i].val].term;
+            break;
+        }
+        *dst++ = Rest[i + 1];
+    }
+
+    if (!beam_load_find_literal(S, keys, &lit)) {
+        lit = beam_load_new_literal(S, &hp, 1 + size/2);
+        sys_memcpy(hp, tmp, (1 + size/2) * sizeof(*tmp));
+    }
+    erts_free(ERTS_ALC_T_LOADER_TMP, tmp);
+
+    op->a[0] = Dst;
+    op->a[1] = Live;
+    op->a[2].type = TAG_q;
+    op->a[2].val = lit;
+
+    return op;
+}
+
+// Replace a get_map_elements instruction with a single key to an
+// instruction with one element.
+gen.get_map_element(Fail, Src, Size, Rest) {
+    GenOp* op;
+    GenOpArg Key;
+    Eterm key_term;
+
+    ASSERT(Size.type == TAG_u);
+
+    $NewBeamOp(S, op);
+    op->a[0] = Fail;
+    op->a[1] = Src;
+    op->a[2] = Rest[0];
+
+    Key = Rest[0];
+    key_term = beam_load_get_literal(S, Key);
+    if (is_value(key_term)) {
+        $BeamOpNameArity(op, i_get_map_element_hash, 5);
+        op->a[3].type = TAG_u;
+        op->a[3].val = (BeamInstr) hashmap_make_hash(key_term);
+        op->a[4] = Rest[1];
+    } else {
+        $BeamOpNameArity(op, i_get_map_element, 4);
+        op->a[3] = Rest[1];
+    }
+    return op;
+}
+
+gen.get_map_elements(Fail, Src, Size, Rest) {
+    GenOp* op;
+    Uint i;
+    GenOpArg* dst;
+    Eterm key_term;
+
+    ASSERT(Size.type == TAG_u);
+
+    $NewBeamOp(S, op);
+    $BeamOpNameArity(op, i_get_map_elements, 3);
+    $BeamOpArity(op, 3 + 3*(Size.val/2));
+    op->a[0] = Fail;
+    op->a[1] = Src;
+    op->a[2].type = TAG_u;
+    op->a[2].val = 3*(Size.val/2);
+
+    dst = op->a+3;
+    for (i = 0; i < Size.val / 2; i++) {
+        dst[0] = Rest[2*i];
+        dst[1] = Rest[2*i+1];
+        dst[2].type = TAG_u;
+        key_term = beam_load_get_literal(S, dst[0]);
+        dst[2].val = (BeamInstr) hashmap_make_hash(key_term);
+        dst += 3;
+    }
+    return op;
+}
+
+gen.has_map_fields(Fail, Src, Size, Rest) {
+    GenOp* op;
+    Uint i;
+    Uint n;
+
+    ASSERT(Size.type == TAG_u);
+    n = Size.val;
+
+    $NewBeamOp(S, op);
+    $BeamOpNameArity(op, get_map_elements, 3);
+    $BeamOpArity(op, 3 + 2*n);
+
+    op->a[0] = Fail;
+    op->a[1] = Src;
+    op->a[2].type = TAG_u;
+    op->a[2].val = 2*n;
+
+    for (i = 0; i < n; i++) {
+        op->a[3+2*i] = Rest[i];
+        op->a[3+2*i+1].type = TAG_x;
+        op->a[3+2*i+1].val = SCRATCH_X_REG; /* Ignore result */
+    }
+    return op;
+}
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index f3ab8bf48d..49bd7a128c 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -144,35 +144,35 @@ init3 y y y
 select_val S=aiq Fail=f Size=u Rest=* => const_select_val(S, Fail, Size, Rest)
 
 select_val S=s Fail=f Size=u Rest=* | use_jump_tab(Size, Rest) => \
-  gen_jump_tab(S, Fail, Size, Rest)
+  jump_tab(S, Fail, Size, Rest)
 
 is_integer Fail=f S | select_val S=s Fail=f Size=u Rest=* | use_jump_tab(Size, Rest) => \
-  gen_jump_tab(S, Fail, Size, Rest)
+  jump_tab(S, Fail, Size, Rest)
 
 is_integer TypeFail=f S | select_val S=s Fail=f Size=u Rest=* | \
 	   mixed_types(Size, Rest) => \
-  gen_split_values(S, TypeFail, Fail, Size, Rest)
+  split_values(S, TypeFail, Fail, Size, Rest)
 
 select_val S=s Fail=f Size=u Rest=* | mixed_types(Size, Rest) => \
-  gen_split_values(S, Fail, Fail, Size, Rest)
+  split_values(S, Fail, Fail, Size, Rest)
 
 is_integer Fail=f S | select_val S=d Fail=f Size=u Rest=* | \
-  fixed_size_values(Size, Rest) => gen_select_val(S, Fail, Size, Rest)
+  fixed_size_values(Size, Rest) => select_val(S, Fail, Size, Rest)
 
 is_atom Fail=f S | select_val S=d Fail=f Size=u Rest=* | \
-  fixed_size_values(Size, Rest) => gen_select_val(S, Fail, Size, Rest)
+  fixed_size_values(Size, Rest) => select_val(S, Fail, Size, Rest)
 
 select_val S=s Fail=f Size=u Rest=* | floats_or_bignums(Size, Rest) => \
-  gen_select_literals(S, Fail, Size, Rest)
+  select_literals(S, Fail, Size, Rest)
 
 select_val S=d Fail=f Size=u Rest=* | fixed_size_values(Size, Rest) => \
-  gen_select_val(S, Fail, Size, Rest)
+  select_val(S, Fail, Size, Rest)
 
 is_tuple Fail=f S | select_tuple_arity S=d Fail=f Size=u Rest=* => \
-  gen_select_tuple_arity(S, Fail, Size, Rest)
+  select_tuple_arity(S, Fail, Size, Rest)
 
 select_tuple_arity S=d Fail=f Size=u Rest=* => \
-  gen_select_tuple_arity(S, Fail, Size, Rest)
+  select_tuple_arity(S, Fail, Size, Rest)
 
 i_select_val_bins xy f? I
 
@@ -294,13 +294,13 @@ move_jump f c r
 move X1=x Y1=y | move X2=x Y2=y | succ(Y1, Y2) => \
     move_window2 X1 X2 Y1
 
-move_window2 X1 X2 Y1 | move X3=x Y3=y | offset(Y1, Y3, 2) => \
+move_window2 X1 X2 Y1 | move X3=x Y3=y | is_offset(Y1, Y3, 2) => \
     move_window3 X1 X2 X3 Y1
 
-move_window3 X1 X2 X3 Y1 | move X4=x Y4=y | offset(Y1, Y4, 3) => \
+move_window3 X1 X2 X3 Y1 | move X4=x Y4=y | is_offset(Y1, Y4, 3) => \
     move_window4 X1 X2 X3 X4 Y1
 
-move_window4 X1 X2 X3 X4 Y1=y | move X5=x Y5=y | offset(Y1, Y5, 4) => \
+move_window4 X1 X2 X3 X4 Y1=y | move X5=x Y5=y | is_offset(Y1, Y5, 4) => \
     move_window5 X1 X2 X3 X4 X5 Y1
 
 move_window2 x x y
@@ -424,8 +424,8 @@ label L | wait_timeout Fail Src | smp_already_locked(L) => \
     label L | wait_timeout_locked Src Fail
 wait_timeout Fail Src => wait_timeout_unlocked Src Fail
 
-wait_timeout_unlocked Src=aiq Fail => gen_literal_timeout(Fail, Src)
-wait_timeout_locked Src=aiq Fail => gen_literal_timeout_locked(Fail, Src)
+wait_timeout_unlocked Src=aiq Fail => literal_timeout(Fail, Src)
+wait_timeout_locked Src=aiq Fail => literal_timeout_locked(Fail, Src)
 
 label L | wait Fail | smp_already_locked(L) => label L | wait_locked Fail
 wait Fail => wait_unlocked Fail
@@ -787,7 +787,7 @@ is_boolean Fail=f ac => jump Fail
 is_boolean f? xy
 %hot
 
-is_function2 Fail=f Fun Arity => gen_is_function2(Fail, Fun, Arity)
+is_function2 Fail=f Fun Arity => is_function2(Fail, Fun, Arity)
 
 %cold
 cold_is_function2 f? x x
@@ -1028,9 +1028,9 @@ bif0 u$bif:erlang:node/0 Dst=d => node Dst
 bif1 Fail=f Bif=u$bif:erlang:hd/1 Src=x Dst=x => is_nonempty_list_get_hd Fail Src Dst
 bif1 Fail=f Bif=u$bif:erlang:tl/1 Src=x Dst=x => is_nonempty_list_get_tl Fail Src Dst
 
-bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => gen_get(Src, Dst)
+bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => get(Src, Dst)
 
-bif2 Jump=j u$bif:erlang:element/2 S1=s S2=xy Dst=d => gen_element(Jump, S1, S2, Dst)
+bif2 Jump=j u$bif:erlang:element/2 S1=s S2=xy Dst=d => element(Jump, S1, S2, Dst)
 
 bif1 p Bif S1 Dst         => i_bif1_body S1 Bif Dst
 bif1 Fail=f Bif S1 Dst    => i_bif1 S1 Fail Bif Dst
@@ -1110,7 +1110,7 @@ i_call_fun_last t Q
 # As a further optimization, the we try to move the fun to its
 # final destination directly.
 
-make_fun2 OldIndex=u => gen_make_fun2(OldIndex)
+make_fun2 OldIndex=u => make_fun2(OldIndex)
 
 move_fun/2
 move_fun Fun X0 | move X0 Dst | move Src X0 => move Fun Dst | move Src X0
@@ -1142,11 +1142,11 @@ bs_start_match2 Fail Bin X Y D => i_bs_start_match2 Bin Fail X Y D
 i_bs_start_match2 xy f t t d
 
 bs_save2 Y=y Index => move Y x | bs_save2 x Index
-bs_save2 Reg Index => gen_bs_save(Reg, Index)
+bs_save2 Reg Index => bs_save(Reg, Index)
 i_bs_save2 x t
 
 bs_restore2 Y=y Index => move Y x | bs_restore2 x Index
-bs_restore2 Reg Index => gen_bs_restore(Reg, Index)
+bs_restore2 Reg Index => bs_restore(Reg, Index)
 i_bs_restore2 x t
 
 bs_context_to_binary Y=y | line L | badmatch Y => \
@@ -1168,7 +1168,7 @@ i_bs_match_string xy f W W
 
 # Fetching integers from binaries.
 bs_get_integer2 Fail=f Ms=xy Live=u Sz=sq Unit=u Flags=u Dst=d => \
-			gen_get_integer2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
+			get_integer2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
 
 i_bs_get_integer_small_imm Ms Bits Fail Flags Y=y => \
    i_bs_get_integer_small_imm Ms Bits Fail Flags x | move x Y
@@ -1188,7 +1188,7 @@ i_bs_get_integer_32 xy f? d
 
 # Fetching binaries from binaries.
 bs_get_binary2 Fail=f Ms=xy Live=u Sz=sq Unit=u Flags=u Dst=d => \
-			gen_get_binary2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
+			get_binary2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
 
 i_bs_get_binary_imm2 xy f? t W t d
 i_bs_get_binary2 xy f t? S t d
@@ -1196,7 +1196,7 @@ i_bs_get_binary_all2 xy f? t t d
 
 # Fetching float from binaries.
 bs_get_float2 Fail=f Ms=xy Live=u Sz=s Unit=u Flags=u Dst=d => \
-		gen_get_float2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
+		get_float2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
 
 bs_get_float2 Fail=f Ms=x Live=u Sz=q Unit=u Flags=u Dst=d => jump Fail
 
@@ -1204,8 +1204,7 @@ i_bs_get_float2 xy f? t s t d
 
 # Miscellaneous
 
-bs_skip_bits2 Fail=f Ms=xy Sz=sq Unit=u Flags=u => \
-			gen_skip_bits2(Fail, Ms, Sz, Unit, Flags)
+bs_skip_bits2 Fail=f Ms=xy Sz=sq Unit=u Flags=u => skip_bits2(Fail, Ms, Sz, Unit, Flags)
 
 i_bs_skip_bits_imm2 f? xy W
 i_bs_skip_bits2 xy xy f? t
@@ -1358,7 +1357,7 @@ i_bs_private_append j? t s S x
 #
 
 bs_put_integer Fail=j Sz=sq Unit=u Flags=u Src=s => \
-			gen_put_integer(Fail, Sz, Unit, Flags, Src)
+			put_integer(Fail, Sz, Unit, Flags, Src)
 
 i_new_bs_put_integer j? S t s
 i_new_bs_put_integer_imm xyc j? W t
@@ -1396,7 +1395,7 @@ i_bs_validate_unicode Fail Src=c => move Src x | i_bs_validate_unicode Fail x
 bs_put_float Fail Sz=q Unit Flags Val => badarg Fail
 
 bs_put_float Fail=j Sz=s Unit=u Flags=u Src=s => \
-			gen_put_float(Fail, Sz, Unit, Flags, Src)
+			put_float(Fail, Sz, Unit, Flags, Src)
 
 i_new_bs_put_float j? S t s
 i_new_bs_put_float_imm j? W t s
@@ -1406,7 +1405,7 @@ i_new_bs_put_float_imm j? W t s
 #
 
 bs_put_binary Fail=j Sz=s Unit=u Flags=u Src=s => \
-			gen_put_binary(Fail, Sz, Unit, Flags, Src)
+			put_binary(Fail, Sz, Unit, Flags, Src)
 
 # In unoptimized code, the binary argument could be a literal. (In optimized code,
 # there would be a bs_put_string instruction.)
@@ -1455,15 +1454,12 @@ i_fmul l l l
 i_fdiv l l l
 i_fnegate l l
 
-fclearerror | no_fpe_signals() =>
-fcheckerror p | no_fpe_signals() =>
-
-%unless NO_FPE_SIGNALS
-fcheckerror p => i_fcheckerror
-
-i_fcheckerror
-fclearerror
-%endif
+#
+# FPE signals were disabled in OTP 21 and we don't intend to ever
+# enable them again.
+#
+fclearerror =>
+fcheckerror p =>
 
 %hot
 
@@ -1527,7 +1523,7 @@ sorted_put_map_exact Fail Src Dst Live Size Rest=* => \
    move Src x | update_map_exact x Fail Dst Live Size Rest
 
 new_map Dst Live Size Rest=* | is_small_map_literal_keys(Size, Rest) => \
-   gen_new_small_map_lit(Dst, Live, Size, Rest)
+   new_small_map_lit(Dst, Live, Size, Rest)
 
 new_map d t I
 i_new_small_map_lit d t q
@@ -1541,15 +1537,14 @@ is_map f? xy
 
 ## Transform has_map_fields #{ K1 := _, K2 := _ } to has_map_elements
 
-has_map_fields Fail Src Size Rest=* => \
-   gen_has_map_fields(Fail, Src, Size, Rest)
+has_map_fields Fail Src Size Rest=* => has_map_fields(Fail, Src, Size, Rest)
 
 ## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 }
 
 get_map_elements Fail Src Size=u==2 Rest=* => \
-    gen_get_map_element(Fail, Src, Size, Rest)
+    get_map_element(Fail, Src, Size, Rest)
 get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \
-   gen_get_map_elements(Fail, Src, Size, Rest)
+   get_map_elements(Fail, Src, Size, Rest)
 
 i_get_map_elements f? s I
 
@@ -1583,12 +1578,12 @@ gc_bif2 Fail Live u$bif:erlang:sminus/2 S1 S2 Dst => \
 #
 
 gen_plus p Live Int=i Reg=d Dst => \
-	gen_increment(Reg, Int, Dst)
+	increment(Reg, Int, Dst)
 gen_plus p Live Reg=d Int=i Dst => \
-	gen_increment(Reg, Int, Dst)
+	increment(Reg, Int, Dst)
 
 gen_minus p Live Reg=d Int=i Dst | negation_is_small(Int) => \
-	gen_increment_from_minus(Reg, Int, Dst)
+	increment_from_minus(Reg, Int, Dst)
 
 #
 # Arithmetic instructions.
@@ -1597,7 +1592,7 @@ gen_minus p Live Reg=d Int=i Dst | negation_is_small(Int) => \
 # It is OK to swap arguments for '+' in a guard. It is also
 # OK to turn minus into plus in a guard.
 gen_plus Fail=f Live S1=c S2 Dst => i_plus S2 S1 Fail Dst
-gen_minus Fail=f Live S1 S2=i Dst => gen_plus_from_minus(Fail, Live, S1, S2, Dst)
+gen_minus Fail=f Live S1 S2=i Dst => plus_from_minus(Fail, Live, S1, S2, Dst)
 
 gen_plus Fail Live S1 S2 Dst => i_plus S1 S2 Fail Dst
 
diff --git a/erts/emulator/beam/predicates.tab b/erts/emulator/beam/predicates.tab
new file mode 100644
index 0000000000..bbff57172b
--- /dev/null
+++ b/erts/emulator/beam/predicates.tab
@@ -0,0 +1,269 @@
+// -*- c -*-
+//
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2020. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// %CopyrightEnd%
+//
+
+pred.is_offset(X, Y, Offset) {
+    return X.type == Y.type && X.val + Offset == Y.val;
+}
+
+
+pred.succ(X, Y) {
+    return X.type == Y.type && X.val + 1 == Y.val;
+}
+
+pred.succ3(X, Y) {
+    return X.type == Y.type && X.val + 3 == Y.val;
+}
+
+pred.succ4(X, Y) {
+    return X.type == Y.type && X.val + 4 == Y.val;
+}
+
+pred.never() {
+    return 0;
+}
+
+pred.compiled_with_otp_20_or_higher() {
+    return S->otp_20_or_higher;
+}
+
+// Test whether the following two moves are independent:
+//
+//    move Src1 Dst1
+//    move Src2 Dst2
+//
+pred.independent_moves(Src1, Dst1, Src2, Dst2) {
+    return (Src1.type != Dst2.type || Src1.val != Dst2.val) &&
+        (Src2.type != Dst1.type || Src2.val != Dst1.val) &&
+        (Dst1.type != Dst2.type ||Dst1.val != Dst2.val);
+}
+
+
+// Test that the two registers are distinct.
+pred.distinct(Reg1, Reg2) {
+    return Reg1.type != Reg2.type || Reg1.val != Reg2.val;
+}
+
+// Test whether a jump table can be used.
+pred.use_jump_tab(Size, Rest) {
+    Sint min, max;
+    Sint i;
+
+    if (Size.val < 2 || Size.val % 2 != 0) {
+        return 0;
+    }
+
+    if (Rest[0].type != TAG_i || Rest[1].type != TAG_f) {
+        /* Atoms. Can't use a jump table. */
+        return 0;
+    }
+
+    min = max = Rest[0].val;
+    for (i = 2; i < Size.val; i += 2) {
+        if (Rest[i].type != TAG_i || Rest[i+1].type != TAG_f) {
+            return 0;
+        }
+        if (Rest[i].val < min) {
+            min = Rest[i].val;
+        } else if (max < Rest[i].val) {
+            max = Rest[i].val;
+        }
+    }
+
+    return max - min <= Size.val;
+}
+
+// Test whether all values in a table are either floats or bignums.
+pred.floats_or_bignums(Size, Rest) {
+    int i;
+
+    if (Size.val < 2 || Size.val % 2 != 0) {
+        return 0;
+    }
+
+    for (i = 0; i < Size.val; i += 2) {
+        if (Rest[i].type != TAG_q) {
+            return 0;
+        }
+        if (Rest[i+1].type != TAG_f) {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+
+// Test whether all values in a table have a fixed size.
+pred.fixed_size_values(Size, Rest) {
+    int i;
+
+    if (Size.val < 2 || Size.val % 2 != 0) {
+        return 0;
+    }
+
+    for (i = 0; i < Size.val; i += 2) {
+        if (Rest[i+1].type != TAG_f) {
+            return 0;
+        }
+        switch (Rest[i].type) {
+        case TAG_a:
+        case TAG_i:
+        case TAG_v:
+            break;
+        case TAG_q:
+            return is_float(S->literals[Rest[i].val].term);
+        default:
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+// Test whether a table has mixe types.
+pred.mixed_types(Size, Rest) {
+    int i;
+    Uint type;
+
+    if (Size.val < 2 || Size.val % 2 != 0) {
+        return 0;
+    }
+
+    type = Rest[0].type;
+    for (i = 0; i < Size.val; i += 2) {
+        if (Rest[i].type != type) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+// Test whether register Reg is killed by a make_fun instruction that
+// creates the fun given by index idx.
+pred.is_killed_by_make_fun(Reg, idx) {
+    Uint num_free;
+
+    if (idx.val >= S->num_lambdas) {
+        /* Invalid index. Ignore the error for now. */
+        return 0;
+    } else {
+        num_free = S->lambdas[idx.val].num_free;
+        return Reg.type == TAG_x && num_free <= Reg.val;
+    }
+}
+
+// Test whether Bif is "heavy" and should always go through its export entry.
+pred.is_heavy_bif(Bif) {
+    Export *ep;
+
+    if (Bif.type != TAG_u || Bif.val >= S->num_imports) {
+        return 0;
+    }
+
+    if ((ep = S->import[Bif.val].bif) != 0) {
+        return bif_table[ep->bif_number].kind == BIF_KIND_HEAVY;
+    }
+
+    return 0;
+}
+
+// Test whether the given literal is a map.
+pred.literal_is_map(Lit) {
+    Eterm term;
+
+    ASSERT(Lit.type == TAG_q);
+    term = S->literals[Lit.val].term;
+    return is_map(term);
+}
+
+// Predicate to test whether all of the given new small map keys are literals
+pred.is_small_map_literal_keys(Size, Rest) {
+    if (Size.val > MAP_SMALL_MAP_LIMIT) {
+        return 0;
+    }
+
+    /*
+     * Operations with non-literals have always only one key.
+     */
+    if (Size.val != 2) {
+        return 1;
+    }
+
+    switch (Rest[0].type) {
+    case TAG_a:
+    case TAG_i:
+    case TAG_n:
+    case TAG_q:
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+// Test whether the given literal is an empty map.
+pred.is_empty_map(Lit) {
+    Eterm term;
+
+    if (Lit.type != TAG_q) {
+        return 0;
+    }
+    term = S->literals[Lit.val].term;
+    return is_flatmap(term) && flatmap_get_size(flatmap_val(term)) == 0;
+}
+
+// Test whether a binary construction is too big.
+pred.binary_too_big(Size) {
+    return Size.type == TAG_o ||
+        (Size.type == TAG_u && ((Size.val >> (8*sizeof(Uint)-3)) != 0));
+}
+
+
+// Test whether the negation of the given number is small.
+pred.negation_is_small(Int) {
+    /*
+     * Check for the rare case of overflow in BeamInstr (UWord) -> Sint.
+     * Cast to the correct type before using IS_SSMALL (Sint).
+     */
+    return Int.type == TAG_i &&
+        !(Int.val & ~((((BeamInstr)1) << ((sizeof(Sint)*8)-1))-1)) &&
+        IS_SSMALL(-((Sint)Int.val));
+}
+
+// Mark this label. Always succeeds.
+pred.smp_mark_target_label(L) {
+    ASSERT(L.type == TAG_f);
+    S->labels[L.val].looprec_targeted = 1;
+    return 1;
+}
+
+// Test whether this label was targeted by a loop_rec/2 instruction.
+pred.smp_already_locked(L) {
+    ASSERT(L.type == TAG_u);
+    return S->labels[L.val].looprec_targeted;
+}
+
+
+// Sort map keys. Always succeeds unless the instruction contains
+// invalid map keys (in which case loading will fail).
+pred.map_key_sort(Size, Rest) {
+    return beam_load_map_key_sort(S, Size, Rest);
+}
diff --git a/erts/emulator/internal_doc/beam_makeops.md b/erts/emulator/internal_doc/beam_makeops.md
index 1ec94d4ff9..f145cda964 100644
--- a/erts/emulator/internal_doc/beam_makeops.md
+++ b/erts/emulator/internal_doc/beam_makeops.md
@@ -986,33 +986,55 @@ instruction, it must only be used for a destination register.)
 
 * `o` - Overflow.  An untagged integer that does not fit in a machine word.
 
-#### Guard constraints ####
+#### Guard constraints (predicates) ####
 
 If the constraints described so far is not enough, additional
-constraints can be written in C in `beam_load.c` and be called as a
-guard function on the left side of the transformation.  If the guard
-function returns a non-zero value, the matching of the rule will
-continue, otherwise the match will fail.  For example:
+constraints can be implemented in C and be called as a guard function
+on the left side of the transformation.  If the guard function returns
+a non-zero value, the matching of the rule will continue, otherwise
+the match will fail.  For example:
 
     ensure_map Lit=q | literal_is_map(Lit) =>
 
 The guard test `literal_is_map/1` tests whether the given literal is a map.
 If the literal is a map, the instruction is unnecessary and can be removed.
 
-It is outside the scope for this document to describe in detail how such
-guard functions are written, but for the curious here is the implementation
-of `literal_is_map()`:
+Such guard tests are also called predicates.  At the time of writing, all
+predicates are defined in the file `predicates.tab`.
 
-    static int
-    literal_is_map(LoaderState* stp, GenOpArg Lit)
-    {
+It is outside the scope for this document to describe in detail how
+predicates are implemented because it requires knowledge of the
+internal loader data structures, but here is the implementation of
+`literal_is_map()`:
+
+    pred.literal_is_map(Lit) {
         Eterm term;
 
         ASSERT(Lit.type == TAG_q);
-        term = stp->literals[Lit.val].term;
+        term = S->literals[Lit.val].term;
         return is_map(term);
     }
 
+The `pred.` prefix tells **beam\_makeops** that this function is a
+predicate.  Without the prefix, it would have been interpreted as the
+implementation of an instruction (described in **Defining the
+implementation**).
+
+Predicate functions have a magic variabled called `S`, which is a
+pointer to a state struct.  In the example, `S->literals[Lit.val].term`
+is used to retrieve the actual term for the literal.
+
+At the time of writing, the expanded C code generated by
+**beam\_makeops** looks like this:
+
+    static int literal_is_map(LoaderState* S, GenOpArg Lit) {
+      Eterm term;
+
+      ASSERT(Lit.type == TAG_q);
+      term = S->literals[Lit.val].term;
+      return is_map(term);;
+    }
+
 #### Handling instruction with variable number of operands ####
 
 Some instructions, such as `select_val/3`, essentially has a variable
@@ -1100,24 +1122,24 @@ use as a temporary X register.
 #### Function call on the right side ####
 
 Transformations that are not possible to describe with the rule
-language as described here can be written as a C function in
-`beam_load.c` and called from the right side of a transformation.  The
-left side of the transformation will perform the match and bind
-operands to variables.  The variables can then be passed to a
-generator function on the right side.  For example:
+language as described here can be implemented as a generator function
+in C and called from the right side of a transformation.  The left
+side of the transformation will perform the match and bind operands to
+variables.  The variables can then be passed to a generator function
+on the right side.  For example:
 
     bif2 Fail=j u$bif:erlang:element/2 Index=s Tuple=xy Dst=d => \
-        gen_element(Jump, Index, Tuple, Dst)
+        element(Jump, Index, Tuple, Dst)
 
 This transformation rule matches a call to the BIF `element/2`.
-The operands will be captured and the function `gen_element()` will
+The operands will be captured and the generator function `element()` will
 be called.
 
-`gen_element()` will produce one of two instructions depending
-on `Index`.  If `Index` is an integer in the range from 1 up to
-the maximum tuple size, the instruction `i_fast_element/2` will
-be produced, otherwise the instruction `i_element/4` will be
-produced.  The corresponding specific instructions are:
+The `element()` generator will produce one of two instructions
+depending on `Index`.  If `Index` is an integer in the range from 1 up
+to the maximum tuple size, the instruction `i_fast_element/2` will be
+produced, otherwise the instruction `i_element/4` will be produced.
+The corresponding specific instructions are:
 
     i_fast_element xy j? I d
     i_element xy j? s d
@@ -1128,31 +1150,29 @@ already an untagged integer.  It also knows that the index is at least
 instruction will have to fetch the index from a register, test that it
 is an integer, and untag the integer.
 
+At the time of writing, all generators functions were defined in the
+file `generators.tab`.
+
 It is outside the scope of this document to describe in detail how
-generator functions are written, but for the curious, here is the
-implementation of `gen_element()`:
+generator functions are written, but here is the
+implementation of `element()`:
 
-    static GenOp*
-    gen_element(LoaderState* stp, GenOpArg Fail,
-       GenOpArg Index, GenOpArg Tuple, GenOpArg Dst)
-    {
+    gen.element(Fail, Index, Tuple, Dst) {
         GenOp* op;
 
-        NEW_GENOP(stp, op);
-        op->arity = 4;
-        op->next = NULL;
+        $NewBeamOp(S, op);
 
         if (Index.type == TAG_i && Index.val > 0 &&
-           Index.val <= ERTS_MAX_TUPLE_SIZE &&
-           (Tuple.type == TAG_x || Tuple.type == TAG_y)) {
-            op->op = genop_i_fast_element_4;
+            Index.val <= ERTS_MAX_TUPLE_SIZE &&
+            (Tuple.type == TAG_x || Tuple.type == TAG_y)) {
+            $BeamOpNameArity(op, i_fast_element, 4);
             op->a[0] = Tuple;
             op->a[1] = Fail;
             op->a[2].type = TAG_u;
             op->a[2].val = Index.val;
             op->a[3] = Dst;
         } else {
-            op->op = genop_i_element_4;
+            $BeamOpNameArity(op, i_element, 4);
             op->a[0] = Tuple;
             op->a[1] = Fail;
             op->a[2] = Index;
@@ -1162,6 +1182,15 @@ implementation of `gen_element()`:
         return op;
     }
 
+The `gen.` prefix tells **beam\_makeops** that this function is a
+generator.  Without the prefix, it would have been interpreted as the
+implementation of an instruction (described in **Defining the
+implementation**).
+
+Generator functions have a magic variabled called `S`, which is a
+pointer to a state struct.  In the example, `S` is used in the invocation
+of the `NewBeamOp` macro.
+
 ### Defining the implementation ###
 
 The actual implementation of instructions are also defined in `.tab`
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index f951a33567..4b71f6537c 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -97,6 +97,7 @@ my @obsolete;
 # Instructions and micro instructions implemented in C.
 my %c_code;                     # C code block, location, arguments.
 my %c_code_used;                # Used or not.
+my %c_param_types;              # Types for predicates and generators.
 
 # Definitions for instructions combined from micro instructions.
 my %combined_instrs;
@@ -109,7 +110,6 @@ my %unnumbered;
 
 my %is_transformed;
 
-
 #
 # Pre-processor.
 #
@@ -597,17 +597,7 @@ sub emulator_output {
     $name = "$outdir/beam_opcodes.c";
     open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
     comment('C');
-    print "#ifdef HAVE_CONFIG_H\n";
-    print "#  include \"config.h\"\n";
-    print "#endif\n\n";
-    print '#include "sys.h"', "\n";
-    print '#include "erl_vm.h"', "\n";
-    print '#include "export.h"', "\n";
-    print '#include "erl_process.h"', "\n";
-    print '#include "bif.h"', "\n";
-    print '#include "erl_atom_table.h"', "\n";
-    print '#include "beam_load.h"', "\n";
-    print "\n";
+    include_files();
 
     print "const char tag_to_letter[] = {\n  ";
     for ($i = 0; $i < length($genop_types); $i++) {
@@ -864,15 +854,19 @@ sub emulator_output {
     # Extension of transform engine.
     #
 
-    $name = "$outdir/beam_tr_funcs.h";
+    $name = "$outdir/beam_transform.c";
     open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
     comment('C');
-    tr_gen_call(@call_table);
-
-    $name = "$outdir/beam_pred_funcs.h";
-    open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
-    comment('C');
-    tr_gen_call(@pred_table);
+    include_files();
+    print '#include "erl_term.h"', "\n";
+    print '#include "erl_map.h"', "\n";
+    print '#include "big.h"', "\n";
+    print '#include "beam_transform_helpers.h"', "\n";
+    print "\n";
+    gen_tr_code('pred.');
+    gen_tr_func('int', 'erts_beam_eval_predicate', @pred_table);
+    gen_tr_code('gen.');
+    gen_tr_func('GenOp*', 'erts_beam_execute_transform', @call_table);
 
     #
     # Implementation of operations for emulator.
@@ -2469,10 +2463,12 @@ sub tr_gen_from {
 	    my $var;
 	    my @args;
             my @vars;
+            my @param_types;
 
 	    foreach $var (@ops) {
                 if ($var =~ /^-?\d+$/) {
                     push @args, $var;
+                    push @param_types, 'Uint';
                     next;
                 }
 		error($where, "'$var' unbound")
@@ -2480,11 +2476,16 @@ sub tr_gen_from {
                 push @vars, $var;
 		if ($var_type{$var} eq 'scalar') {
 		    push(@args, "var[$var{$var}]");
+                    push @param_types, 'GenOpArg';
 		} else {
 		    push(@args, "rest_args");
+                    push @param_types, 'GenOpArg*';
 		}
 	    }
-	    my $pi = tr_next_index(\@{pred_table}, \%pred_table, $name, @args);
+            my $c_name = "pred.$name";
+            $c_param_types{$c_name} = \@param_types;
+            $c_code_used{$c_name} = 1;
+	    my $pi = next_tr_index(\@{pred_table}, \%pred_table, $name, @args);
             my $op = make_op("$name()", 'pred', $pi);
             my @slots = grep(/^\d+/, map { $var{$_} } @vars);
 	    op_slot_usage($op, @slots);
@@ -2654,18 +2655,24 @@ sub tr_gen_to {
 	    $name = $1;
 	    my $var;
 	    my(@args);
+            my @param_types;
 
 	    foreach $var (@ops) {
 		error($where, "variable '$var' unbound")
 		    unless defined $var{$var};
 		if ($var_type{$var} eq 'scalar') {
-		    push(@args, "var[$var{$var}]");
+		    push @args, "var[$var{$var}]";
+                    push @param_types, 'GenOpArg';
 		} else {
-		    push(@args, "rest_args");
+		    push @args, "rest_args";
+                    push @param_types, 'GenOpArg*';
 		}
 	    }
+            my $c_name = "gen.$name";
+            $c_param_types{$c_name} = \@param_types;
+            $c_code_used{$c_name} = 1;
 	    pop(@code);	# Get rid of 'commit' instruction
-	    my $index = tr_next_index(\@call_table, \%call_table,
+	    my $index = next_tr_index(\@call_table, \%call_table,
 				      $name, @args);
 	    my $op = make_op("$name()", 'call_end', $index);
 	    my @slots = grep(/^\d+/, map { $var{$_} } @ops);
@@ -3027,6 +3034,39 @@ sub tr_remove_unused {
     }
 }
 
+sub gen_tr_code {
+    my($prefix) = @_;
+
+    foreach my $name (sort keys %c_code) {
+        if (index($name, $prefix) == 0) {
+            my $func_name = $name;
+            $func_name =~ s/^$prefix//;
+            my($block,$where,@params) = @{$c_code{$name}};
+            my %bindings;
+            $block = eval { expand_all($block, \%bindings) };
+            unless (defined $block) {
+                warn $@;
+                error("... from the body of $name at $where");
+            }
+            my $head = 'static ';
+            $head .= $prefix eq 'pred.' ? 'int' : 'GenOp*';
+            $head .= " $func_name(LoaderState* S";
+            my(@param_types);
+            if (defined $c_param_types{$name}) {
+                @param_types = @{$c_param_types{$name}};
+            } else {
+                @param_types = ('GenOpArg') x @params;
+            }
+            for (my $i = 0; $i < @params; $i++) {
+                $head .= ", $param_types[$i] $params[$i]";
+            }
+            $head .= ") {\n";
+            my $code = $head . "$block;\n}\n";
+            print_indented_code($code);
+        }
+    }
+}
+
 sub code_len {
     my($sum) = 0;
     my($ref);
@@ -3063,9 +3103,9 @@ sub starred_comment {
     "\n/*" . join("\n * ", '', @_) . "\n */\n\n";
 }
 
-sub tr_next_index {
+sub next_tr_index {
     my($lref,$href,$name,@args) = @_;
-    my $code = "RVAL = $name(" . join(', ', 'st', @args) . "); break;\n";
+    my $code = "return $name(" . join(', ', 'st', @args) . ");\n";
     my $index;
 
     if (defined $$href{$code}) {
@@ -3078,11 +3118,29 @@ sub tr_next_index {
     $index;
 }
 
-sub tr_gen_call {
-    my(@call_table) = @_;
-    my($i);
+sub gen_tr_func {
+    my($type,$name,@call_table) = @_;
 
-    for ($i = 0; $i < @call_table; $i++) {
-	print "case $i: $call_table[$i]";
+    print "$type $name(unsigned int op, LoaderState* st, GenOpArg var[], GenOpArg* rest_args) {\n";
+    print "  switch (op) {\n";
+    for (my $i = 0; $i < @call_table; $i++) {
+	print "    case $i: $call_table[$i]";
     }
+    print qq[    default: erts_exit(ERTS_ABORT_EXIT, "$name: invalid op %d\\n", op);];
+    print "  }\n";
+    print "}\n\n";
+}
+
+sub include_files() {
+    print "#ifdef HAVE_CONFIG_H\n";
+    print "#  include \"config.h\"\n";
+    print "#endif\n\n";
+    print '#include "sys.h"', "\n";
+    print '#include "erl_vm.h"', "\n";
+    print '#include "export.h"', "\n";
+    print '#include "erl_process.h"', "\n";
+    print '#include "bif.h"', "\n";
+    print '#include "erl_atom_table.h"', "\n";
+    print '#include "beam_load.h"', "\n";
+    print "\n";
 }
-- 
2.26.2

openSUSE Build Service is sponsored by