Coverage Report

Created: 2026-09-01 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm3/source/m3_compile.h
Line
Count
Source
1
//
2
//  m3_compile.h
3
//
4
//  Created by Steven Massey on 4/17/19.
5
//  Copyright © 2019 Steven Massey. All rights reserved.
6
//
7
8
#ifndef m3_compile_h
9
#define m3_compile_h
10
11
#include "m3_code.h"
12
#include "m3_exec_defs.h"
13
#include "m3_function.h"
14
15
d_m3BeginExternC
16
17
enum
18
{
19
    c_waOp_block                = 0x02,
20
    c_waOp_loop                 = 0x03,
21
    c_waOp_if                   = 0x04,
22
    c_waOp_else                 = 0x05,
23
    c_waOp_throw                = 0x08,
24
    c_waOp_throwRef             = 0x0a,
25
    c_waOp_end                  = 0x0b,
26
    c_waOp_branch               = 0x0c,
27
    c_waOp_branchTable          = 0x0e,
28
    c_waOp_branchIf             = 0x0d,
29
    c_waOp_call                 = 0x10,
30
    c_waOp_returnCall           = 0x12,
31
    c_waOp_returnCallIndirect   = 0x13,
32
    c_waOp_callRef              = 0x14,
33
    c_waOp_returnCallRef        = 0x15,
34
    c_waOp_getLocal             = 0x20,
35
    c_waOp_setLocal             = 0x21,
36
    c_waOp_teeLocal             = 0x22,
37
38
    c_waOp_selectTyped          = 0x1c,
39
    c_waOp_tryTable             = 0x1f,
40
41
    c_waOp_getGlobal            = 0x23,
42
    c_waOp_tableGet             = 0x25,
43
    c_waOp_tableSet             = 0x26,
44
45
    c_waOp_store_f32            = 0x38,
46
    c_waOp_store_f64            = 0x39,
47
48
    c_waOp_i32_const            = 0x41,
49
    c_waOp_i64_const            = 0x42,
50
    c_waOp_f32_const            = 0x43,
51
    c_waOp_f64_const            = 0x44,
52
53
    // the arithmetic the extended-const proposal admits into constant expressions
54
    c_waOp_i32_add              = 0x6a,
55
    c_waOp_i32_sub              = 0x6b,
56
    c_waOp_i32_mul              = 0x6c,
57
    c_waOp_i64_add              = 0x7c,
58
    c_waOp_i64_sub              = 0x7d,
59
    c_waOp_i64_mul              = 0x7e,
60
61
    c_waOp_refNull              = 0xd0,
62
    c_waOp_refIsNull            = 0xd1,
63
    c_waOp_refFunc              = 0xd2,
64
    c_waOp_refAsNonNull         = 0xd4,
65
    c_waOp_brOnNull             = 0xd5,
66
    c_waOp_brOnNonNull          = 0xd6,
67
68
    c_waOp_extended             = 0xfc,
69
70
    c_waOp_memoryInit           = 0xfc08,
71
    c_waOp_memoryCopy           = 0xfc0a,
72
    c_waOp_memoryFill           = 0xfc0b,
73
    c_waOp_tableGrow            = 0xfc0f,
74
    c_waOp_tableSize            = 0xfc10,
75
    c_waOp_tableFill            = 0xfc11,
76
77
    // Highest opcode each operation table actually defines below the reference
78
    // instructions. The tables run past these: with internal ops in DEBUG
79
    // builds, and with the designated 0xd0..0xd2 and 0xfc entries.
80
    c_waOp_lastCore             = 0xc4,     // i64.extend32_s
81
    c_waOp_lastExtended         = 0x11      // table.fill
82
};
83
84
85
0
#define d_FuncRetType(ftype,i)  ((ftype)->types[(i)])
86
0
#define d_FuncArgType(ftype,i)  ((ftype)->types[(ftype)->numRets + (i)])
87
88
//-----------------------------------------------------------------------------------------------------------------------------------
89
90
typedef struct M3CompilationScope
91
{
92
    struct M3CompilationScope *     outer;
93
94
    pc_t                            pc;                 // used by ContinueLoop's
95
    pc_t                            patches;
96
    i32                             depth;
97
    u16                             exitStackIndex;
98
    u16                             blockStackIndex;
99
//    u16                             topSlot;
100
    IM3FuncType                     type;
101
    m3opcode_t                      opcode;
102
    bool                            isPolymorphic;
103
}
104
M3CompilationScope;
105
106
typedef M3CompilationScope *        IM3CompilationScope;
107
108
#if d_m3HasExceptionHandling
109
110
// One catch clause of a try_table, as Compile_TryTable reads it out of the
111
// immediates. The stub that CompileBlock later emits for this clause writes its
112
// own address back into stubSlot, which points at the pointer op_TryTable
113
// reserved for it in the clause table.
114
struct M3Tag;
115
116
typedef struct M3CatchClause
117
{
118
    struct M3Tag *      tag;            // NULL for catch_all / catch_all_ref
119
    void *              stubSlot;
120
    u32                 labelDepth;     // counted from outside the try block
121
    bool                hasRef;         // the _ref forms also hand over an exnref
122
}
123
M3CatchClause;
124
125
#endif
126
127
128
typedef struct
129
{
130
    IM3Runtime          runtime;
131
    IM3Module           module;
132
133
    bytes_t             wasm;
134
    bytes_t             wasmEnd;
135
    bytes_t             lastOpcodeStart;
136
137
    M3CompilationScope  block;
138
139
    IM3Function         function;
140
141
    IM3CodePage         page;
142
143
#ifdef DEBUG
144
    u32                 numEmits;
145
    u32                 numOpcodes;
146
#endif
147
148
    u16                 stackFirstDynamicIndex;     // args and locals are pushed to the stack so that their slot locations can be tracked. the wasm model itself doesn't
149
                                                    // treat these values as being on the stack, so stackFirstDynamicIndex marks the start of the real Wasm stack
150
    u16                 stackIndex;                 // current stack top
151
152
    u16                 slotFirstConstIndex;
153
    u16                 slotMaxConstIndex;          // as const's are encountered during compilation this tracks their location in the "real" stack
154
155
    u16                 slotFirstLocalIndex;
156
    u16                 slotFirstDynamicIndex;      // numArgs + numLocals + numReservedConstants. the first mutable slot available to the compiler.
157
158
    u16                 maxStackSlots;
159
160
    m3slot_t            constants                   [d_m3MaxConstantTableSize];
161
162
    // 'wasmStack' holds slot locations
163
    u16                 wasmStack                   [d_m3MaxFunctionStackHeight];
164
    m3type_t            typeStack                   [d_m3MaxFunctionStackHeight];
165
166
    // 'm3Slots' contains allocation usage counts
167
    u8                  m3Slots                     [d_m3MaxFunctionSlots];
168
169
    u16                 slotMaxAllocatedIndexPlusOne;
170
171
    u16                 regStackIndexPlusOne        [2];
172
173
    m3opcode_t          previousOpcode;
174
175
#if d_m3HasExceptionHandling
176
    // handed from Compile_TryTable to the CompileBlock it is about to start.
177
    // The stubs are emitted from in there because a catch entry has to be
178
    // compiled against the try block's entry state, which is what CompileBlock
179
    // sets up
180
    M3CatchClause *     tryClauses;
181
    u32                 numTryClauses;
182
#endif
183
184
    bool                isInitExpr;                 // walking a constant expression, not a function body
185
186
# if d_m3FoldSetLocal
187
    // the last emitted op, when it is a candidate for local.set destination folding.
188
    // foldPatchPC is NULL whenever anything was emitted (or a branch target captured) since.
189
    pc_t                foldPatchPC;                // address of the candidate's op word in the code stream
190
    m3opcode_t          foldOpcode;
191
    u16                 foldStackIndex;             // stack index of the candidate's pushed result
192
    u8                  foldForm;                   // operand-form index the candidate was emitted with
193
# endif
194
}
195
M3Compilation;
196
197
typedef M3Compilation *                 IM3Compilation;
198
199
typedef M3Result (* M3Compiler)         (IM3Compilation, m3opcode_t);
200
201
202
//-----------------------------------------------------------------------------------------------------------------------------------
203
204
205
typedef struct M3OpInfo
206
{
207
#ifdef DEBUG
208
    const char * const      name;
209
#endif
210
211
    i8                      stackOffset;
212
    u8                      type;
213
214
    // for most operations:
215
    // [0]= top operand in register, [1]= top operand in stack, [2]= both operands in stack
216
    IM3Operation            operations [4];
217
218
    M3Compiler              compiler;
219
}
220
M3OpInfo;
221
222
typedef const M3OpInfo *    IM3OpInfo;
223
224
IM3OpInfo  GetOpInfo  (m3opcode_t opcode);
225
226
// The raw tables, for the DEBUG-only reverse lookup in m3_info.c. Past the last
227
// Wasm opcode they also hold internal operations, which GetOpInfo hides.
228
extern const M3OpInfo   c_operations [];
229
extern const M3OpInfo   c_operationsFC [];
230
extern const u32        c_numOperations;
231
extern const u32        c_numOperationsFC;
232
233
// TODO: This helper should be removed, when MultiValue is implemented
234
static inline
235
0
m3type_t GetSingleRetType(IM3FuncType ftype) {
236
0
    return (ftype && ftype->numRets) ? ftype->types[0] : (u8)c_m3Type_none;
237
0
}
Unexecuted instantiation: fuzzer.c:GetSingleRetType
Unexecuted instantiation: m3_env.c:GetSingleRetType
Unexecuted instantiation: m3_function.c:GetSingleRetType
Unexecuted instantiation: m3_info.c:GetSingleRetType
Unexecuted instantiation: m3_module.c:GetSingleRetType
Unexecuted instantiation: m3_parse.c:GetSingleRetType
Unexecuted instantiation: m3_code.c:GetSingleRetType
Unexecuted instantiation: m3_compile.c:GetSingleRetType
Unexecuted instantiation: m3_core.c:GetSingleRetType
Unexecuted instantiation: m3_validate.c:GetSingleRetType
238
239
static const u16 c_m3RegisterUnallocated = 0;
240
static const u16 c_slotUnused = 0xffff;
241
242
static inline
243
bool  IsRegisterAllocated  (IM3Compilation o, u32 i_register)
244
13.7k
{
245
13.7k
    return (o->regStackIndexPlusOne [i_register] != c_m3RegisterUnallocated);
246
13.7k
}
Unexecuted instantiation: fuzzer.c:IsRegisterAllocated
Unexecuted instantiation: m3_env.c:IsRegisterAllocated
Unexecuted instantiation: m3_function.c:IsRegisterAllocated
Unexecuted instantiation: m3_info.c:IsRegisterAllocated
Unexecuted instantiation: m3_module.c:IsRegisterAllocated
Unexecuted instantiation: m3_parse.c:IsRegisterAllocated
Unexecuted instantiation: m3_code.c:IsRegisterAllocated
m3_compile.c:IsRegisterAllocated
Line
Count
Source
244
13.7k
{
245
13.7k
    return (o->regStackIndexPlusOne [i_register] != c_m3RegisterUnallocated);
246
13.7k
}
Unexecuted instantiation: m3_core.c:IsRegisterAllocated
Unexecuted instantiation: m3_validate.c:IsRegisterAllocated
247
248
static inline
249
bool  IsStackPolymorphic  (IM3Compilation o)
250
52.3k
{
251
52.3k
    return o->block.isPolymorphic;
252
52.3k
}
Unexecuted instantiation: fuzzer.c:IsStackPolymorphic
Unexecuted instantiation: m3_env.c:IsStackPolymorphic
Unexecuted instantiation: m3_function.c:IsStackPolymorphic
Unexecuted instantiation: m3_info.c:IsStackPolymorphic
Unexecuted instantiation: m3_module.c:IsStackPolymorphic
Unexecuted instantiation: m3_parse.c:IsStackPolymorphic
Unexecuted instantiation: m3_code.c:IsStackPolymorphic
m3_compile.c:IsStackPolymorphic
Line
Count
Source
250
52.3k
{
251
52.3k
    return o->block.isPolymorphic;
252
52.3k
}
Unexecuted instantiation: m3_core.c:IsStackPolymorphic
Unexecuted instantiation: m3_validate.c:IsStackPolymorphic
253
254
2.92M
static inline bool  IsRegisterSlotAlias        (u16 i_slot)    { return (i_slot >= d_m3Reg0SlotAlias and i_slot != c_slotUnused); }
Unexecuted instantiation: fuzzer.c:IsRegisterSlotAlias
Unexecuted instantiation: m3_env.c:IsRegisterSlotAlias
Unexecuted instantiation: m3_function.c:IsRegisterSlotAlias
Unexecuted instantiation: m3_info.c:IsRegisterSlotAlias
Unexecuted instantiation: m3_module.c:IsRegisterSlotAlias
Unexecuted instantiation: m3_parse.c:IsRegisterSlotAlias
Unexecuted instantiation: m3_code.c:IsRegisterSlotAlias
m3_compile.c:IsRegisterSlotAlias
Line
Count
Source
254
2.92M
static inline bool  IsRegisterSlotAlias        (u16 i_slot)    { return (i_slot >= d_m3Reg0SlotAlias and i_slot != c_slotUnused); }
Unexecuted instantiation: m3_core.c:IsRegisterSlotAlias
Unexecuted instantiation: m3_validate.c:IsRegisterSlotAlias
255
30.9k
static inline bool  IsFpRegisterSlotAlias      (u16 i_slot)    { return (i_slot == d_m3Fp0SlotAlias);  }
Unexecuted instantiation: fuzzer.c:IsFpRegisterSlotAlias
Unexecuted instantiation: m3_env.c:IsFpRegisterSlotAlias
Unexecuted instantiation: m3_function.c:IsFpRegisterSlotAlias
Unexecuted instantiation: m3_info.c:IsFpRegisterSlotAlias
Unexecuted instantiation: m3_module.c:IsFpRegisterSlotAlias
Unexecuted instantiation: m3_parse.c:IsFpRegisterSlotAlias
Unexecuted instantiation: m3_code.c:IsFpRegisterSlotAlias
m3_compile.c:IsFpRegisterSlotAlias
Line
Count
Source
255
30.9k
static inline bool  IsFpRegisterSlotAlias      (u16 i_slot)    { return (i_slot == d_m3Fp0SlotAlias);  }
Unexecuted instantiation: m3_core.c:IsFpRegisterSlotAlias
Unexecuted instantiation: m3_validate.c:IsFpRegisterSlotAlias
256
0
static inline bool  IsIntRegisterSlotAlias     (u16 i_slot)    { return (i_slot == d_m3Reg0SlotAlias); }
Unexecuted instantiation: fuzzer.c:IsIntRegisterSlotAlias
Unexecuted instantiation: m3_env.c:IsIntRegisterSlotAlias
Unexecuted instantiation: m3_function.c:IsIntRegisterSlotAlias
Unexecuted instantiation: m3_info.c:IsIntRegisterSlotAlias
Unexecuted instantiation: m3_module.c:IsIntRegisterSlotAlias
Unexecuted instantiation: m3_parse.c:IsIntRegisterSlotAlias
Unexecuted instantiation: m3_code.c:IsIntRegisterSlotAlias
Unexecuted instantiation: m3_compile.c:IsIntRegisterSlotAlias
Unexecuted instantiation: m3_core.c:IsIntRegisterSlotAlias
Unexecuted instantiation: m3_validate.c:IsIntRegisterSlotAlias
257
258
259
#ifdef DEBUG
260
    #define M3OP(...)       { __VA_ARGS__ }
261
    #define M3OP_RESERVED   { "reserved" }
262
#else
263
    // Strip-off name
264
    #define M3OP(name, ...) { __VA_ARGS__ }
265
    #define M3OP_RESERVED   { 0 }
266
#endif
267
268
#if d_m3HasFloat
269
    #define M3OP_F          M3OP
270
#elif d_m3NoFloatDynamic
271
    #define M3OP_F(n,o,t,op,...)        M3OP(n, o, t, { op_Unsupported, op_Unsupported, op_Unsupported, op_Unsupported }, __VA_ARGS__)
272
#else
273
    #define M3OP_F(...)     { 0 }
274
#endif
275
276
//-----------------------------------------------------------------------------------------------------------------------------------
277
278
u16         GetMaxUsedSlotPlusOne       (IM3Compilation o);
279
280
M3Result    CompileBlock                (IM3Compilation io, IM3FuncType i_blockType, m3opcode_t i_blockOpcode);
281
282
M3Result    CompileBlockStatements      (IM3Compilation io);
283
M3Result    CompileExpression           (IM3Compilation io, IM3FuncType i_resultType);
284
M3Result    CompileFunction             (IM3Function io_function);
285
286
M3Result    CompileRawFunction          (IM3Module io_module, IM3Function io_function, const void * i_function, const void * i_userdata);
287
288
d_m3EndExternC
289
290
#endif // m3_compile_h