• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rieske / trans / 28318306512

28 Jun 2026 09:45AM UTC coverage: 90.406% (+0.9%) from 89.474%
28318306512

Pull #35

github

rieske
Expand functional tests; TODO valid-C gaps that still fail

New coverage for control flow, expressions, scopes, and calls. Small
semantic/codegen fixes; comment out known language gaps with TODOs.
Pull Request #35: Expand functional tests; TODO valid-C gaps that still fail

16 of 19 new or added lines in 2 files covered. (84.21%)

4 existing lines in 1 file now uncovered.

4881 of 5399 relevant lines covered (90.41%)

220453.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.95
/src/codegen/StackMachine.cpp
1
#include "StackMachine.h"
2

3
#include <stdexcept>
4

5
#include "InstructionSet.h"
6

7
namespace {
8
const int MACHINE_WORD_SIZE = 8;
9
const int STACK_ALIGNMENT = 2 * MACHINE_WORD_SIZE;
10
} // namespace
11

12
namespace codegen {
13

14
StackMachine::StackMachine(std::ostream *ostream, std::unique_ptr<InstructionSet> instructionSet, std::unique_ptr<Amd64Registers> registers) :
292✔
15
        assembly{ostream},
292✔
16
        instructionSet{std::move(instructionSet)},
292✔
17
        registers{std::move(registers)} {}
584✔
18

19
void StackMachine::generatePreamble(std::map<std::string, std::string> constants) {
260✔
20
    assembly.raw(instructionSet->preamble(constants));
260✔
21
}
260✔
22

23
void StackMachine::startProcedure(std::string procedureName, std::vector<Value> values, std::vector<Value> arguments) {
332✔
24

25
    emptyGeneralPurposeRegisters();
332✔
26
    assembly.label(instructionSet->label(procedureName));
332✔
27
    assembly << instructionSet->push(registers->getBasePointer());
332✔
28
    assembly << instructionSet->mov(registers->getStackPointer(), registers->getBasePointer());
332✔
29

30
    for (auto& value : values) {
3,964✔
31
        scopeValues.insert({value.getName(), value});
3,632✔
32
    }
33
    std::size_t integerArgumentRegisterIndex{0};
332✔
34
    std::size_t localIndex{scopeValues.size()};
332✔
35
    int argumentIndex{0};
332✔
36
    for (auto& argument : arguments) {
490✔
37
        if (argument.getType() == Type::INTEGRAL && integerArgumentRegisterIndex < registers->getIntegerArgumentRegisters().size()) {
158✔
38
            Value registerArgument{argument.getName(), static_cast<int>(localIndex), argument.getType(), argument.getSizeInBytes()};
110✔
39
            scopeValues.insert({argument.getName(), registerArgument});
110✔
40
            registers->getIntegerArgumentRegisters()[integerArgumentRegisterIndex]->assign(&scopeValues.at(argument.getName()));
110✔
41
            ++integerArgumentRegisterIndex;
110✔
42
            ++localIndex;
110✔
43
        } else {
110✔
44
            Value stackArgument{argument.getName(), argumentIndex, argument.getType(), argument.getSizeInBytes(), true};
48✔
45
            scopeValues.insert({argument.getName(), stackArgument});
48✔
46
            ++argumentIndex;
48✔
47
        }
48✔
48
    }
49
    localVariableStackSize = 0;
332✔
50
    int savedRegistersStack = registers->getCalleeSavedRegisters().size() * MACHINE_WORD_SIZE;
332✔
51
    localVariableStackSize = (scopeValues.size() - argumentIndex) * MACHINE_WORD_SIZE;
332✔
52
    int stackSize = savedRegistersStack + localVariableStackSize;
332✔
53
    if (stackSize % STACK_ALIGNMENT) {
332✔
54
        assembly << instructionSet->sub(registers->getStackPointer(), localVariableStackSize + MACHINE_WORD_SIZE);
166✔
55
    } else {
56
        assembly << instructionSet->sub(registers->getStackPointer(), localVariableStackSize);
166✔
57
    }
58

59
    pushCalleeSavedRegisters();
332✔
60
}
332✔
61

62
void StackMachine::endProcedure() {
322✔
63
    emptyGeneralPurposeRegisters();
322✔
64
    scopeValues.clear();
322✔
65
    calleeSavedRegisters.clear();
322✔
66
}
322✔
67

68
void StackMachine::label(std::string name) {
512✔
69
    spillGeneralPurposeRegisters();
512✔
70
    assembly.label(instructionSet->label(name));
512✔
71
}
512✔
72

73
void StackMachine::jump(JumpCondition jumpCondition, std::string label) {
528✔
74
    switch (jumpCondition) {
528✔
75
    case JumpCondition::IF_EQUAL:
128✔
76
        assembly << instructionSet->je(label);
128✔
77
        break;
128✔
78
    case JumpCondition::IF_NOT_EQUAL:
22✔
79
        assembly << instructionSet->jne(label);
22✔
80
        break;
22✔
81
    case JumpCondition::IF_ABOVE:
36✔
82
        assembly << instructionSet->jg(label);
36✔
83
        break;
36✔
84
    case JumpCondition::IF_BELOW:
48✔
85
        assembly << instructionSet->jl(label);
48✔
86
        break;
48✔
87
    case JumpCondition::IF_ABOVE_OR_EQUAL:
24✔
88
        assembly << instructionSet->jge(label);
24✔
89
        break;
24✔
90
    case JumpCondition::IF_BELOW_OR_EQUAL:
28✔
91
        assembly << instructionSet->jle(label);
28✔
92
        break;
28✔
93
    case JumpCondition::UNCONDITIONAL:
242✔
94
    default:
95
        spillGeneralPurposeRegisters();
242✔
96
        assembly << instructionSet->jmp(label);
242✔
97
    }
98
}
528✔
99

100
void StackMachine::spillGeneralPurposeRegisters() {
754✔
101
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
11,310✔
102
        storeRegisterValue(*reg);
10,556✔
103
    }
754✔
104
}
754✔
105

106
void StackMachine::spillCallerSavedRegisters() {
754✔
107
    for (auto& reg : registers->getCallerSavedRegisters()) {
6,786✔
108
        storeRegisterValue(*reg);
6,032✔
109
    }
754✔
110
}
754✔
111

112
void StackMachine::assignRegisterToSymbol(Register& reg, Value& symbol) {
1,680✔
113
    if (symbol.isStored()) {
1,680✔
114
        storeRegisterValue(reg);
1,254✔
115
        assembly << instructionSet->mov(memoryBaseRegister(symbol), memoryOffset(symbol), reg);
1,254✔
116
    } else if (&reg != &symbol.getAssignedRegister()) {
426✔
117
        storeRegisterValue(reg);
420✔
118
        Register& valueRegister = symbol.getAssignedRegister();
420✔
119
        storeRegisterValue(valueRegister);
420✔
120
        assembly << instructionSet->mov(valueRegister, reg);
420✔
121
    }
122
}
1,680✔
123

124
void StackMachine::compare(std::string leftSymbolName, std::string rightSymbolName) {
162✔
125
    auto& leftSymbol = scopeValues.at(leftSymbolName);
162✔
126
    auto& rightSymbol = scopeValues.at(rightSymbolName);
162✔
127

128
    if (leftSymbol.isStored() && rightSymbol.isStored()) {
162✔
129
        Register& rightSymbolRegister = assignRegisterTo(rightSymbol);
106✔
130
        assembly << instructionSet->cmp(memoryBaseRegister(leftSymbol), memoryOffset(leftSymbol), rightSymbolRegister);
106✔
131
    } else if (leftSymbol.isStored()) {
56✔
132
        assembly << instructionSet->cmp(memoryBaseRegister(leftSymbol), memoryOffset(leftSymbol), rightSymbol.getAssignedRegister());
16✔
133
    } else if (rightSymbol.isStored()) {
40✔
134
        assembly << instructionSet->cmp(leftSymbol.getAssignedRegister(), memoryBaseRegister(rightSymbol), memoryOffset(rightSymbol));
20✔
135
    } else {
136
        assembly << instructionSet->cmp(leftSymbol.getAssignedRegister(), rightSymbol.getAssignedRegister());
20✔
137
    }
138
}
162✔
139

140
void StackMachine::zeroCompare(std::string symbolName) {
124✔
141
    auto& symbol = scopeValues.at(symbolName);
124✔
142
    if (symbol.isStored()) {
124✔
143
        assembly << instructionSet->cmp(memoryBaseRegister(symbol), memoryOffset(symbol), 0);
114✔
144
    } else {
145
        assembly << instructionSet->cmp(symbol.getAssignedRegister(), 0);
10✔
146
    }
147
}
124✔
148

149
void StackMachine::addressOf(std::string operandName, std::string resultName) {
316✔
150
    auto& operand = scopeValues.at(operandName);
316✔
151
    storeInMemory(operand);
316✔
152
    Register& resultRegister = get64BitRegister();
316✔
153
    assembly << instructionSet->mov(memoryBaseRegister(operand), resultRegister);
316✔
154
    int offset = memoryOffset(operand);
316✔
155
    if (offset) {
316✔
156
        assembly << instructionSet->add(resultRegister, offset);
316✔
157
    }
158
    resultRegister.assign(&scopeValues.at(resultName));
316✔
159
}
316✔
160

161
void StackMachine::dereference(std::string operandName, std::string lvalueName, std::string resultName) {
76✔
162
    auto& operand = scopeValues.at(operandName);
76✔
163
    if (operand.isStored()) {
76✔
164
        assignRegisterTo(operand);
46✔
165
    }
166
    Register& resultRegister = get64BitRegisterExcluding(operand.getAssignedRegister());
76✔
167
    assembly << instructionSet->mov(operand.getAssignedRegister(), 0, resultRegister);
76✔
168
    resultRegister.assign(&scopeValues.at(resultName));
76✔
169

170
    Register& lvalueRegister = get64BitRegisterExcluding(operand.getAssignedRegister());
76✔
171
    assembly << instructionSet->mov(operand.getAssignedRegister(), lvalueRegister);
76✔
172
    lvalueRegister.assign(&scopeValues.at(lvalueName));
76✔
173
}
76✔
174

175
void StackMachine::unaryMinus(std::string operandName, std::string resultName) {
88✔
176
    auto& operand = scopeValues.at(operandName);
88✔
177
    if (operand.isStored()) {
88✔
178
        Register& resultRegister = get64BitRegister();
82✔
179
        assembly << instructionSet->mov(memoryBaseRegister(operand), memoryOffset(operand), resultRegister);
82✔
180
        assembly << instructionSet->neg(resultRegister);
82✔
181
        resultRegister.assign(&scopeValues.at(resultName));
82✔
182
    } else {
183
        Register& operandRegister = operand.getAssignedRegister();
6✔
184
        Register& resultRegister = get64BitRegisterExcluding(operand.getAssignedRegister());
6✔
185
        assembly << instructionSet->mov(operandRegister, resultRegister);
6✔
186
        assembly << instructionSet->neg(resultRegister);
6✔
187
        resultRegister.assign(&scopeValues.at(resultName));
6✔
188
    }
189
}
88✔
190

191
void StackMachine::assign(std::string operandName, std::string resultName) {
282✔
192
    auto& operand = scopeValues.at(operandName);
282✔
193
    auto& result = scopeValues.at(resultName);
282✔
194

195
    if (operand.isStored() && result.isStored()) {
282✔
196
        Register& resultRegister = assignRegisterTo(result);
174✔
197
        assembly << instructionSet->mov(memoryBaseRegister(operand), memoryOffset(operand), resultRegister);
174✔
198
    } else if (operand.isStored()) {
108✔
199
        assembly << instructionSet->mov(memoryBaseRegister(operand), memoryOffset(operand), result.getAssignedRegister());
2✔
200
    } else if (result.isStored()) {
106✔
201
        assembly << instructionSet->mov(operand.getAssignedRegister(), memoryBaseRegister(result), memoryOffset(result));
106✔
202
    } else {
203
        assembly << instructionSet->mov(operand.getAssignedRegister(), result.getAssignedRegister());
×
204
    }
205
}
282✔
206

207
void StackMachine::assignConstant(std::string constant, std::string resultName) {
1,948✔
208
    auto& result = scopeValues.at(resultName);
1,948✔
209
    if (result.isStored()) {
1,948✔
210
        assembly << instructionSet->mov(constant, memoryBaseRegister(result), memoryOffset(result)); // mov dword?
1,948✔
211
    } else {
212
        assembly << instructionSet->mov(constant, result.getAssignedRegister()); // mov dword?
×
213
    }
214
}
1,948✔
215

216
void StackMachine::lvalueAssign(std::string operandName, std::string resultName) {
54✔
217
    auto& operand = scopeValues.at(operandName);
54✔
218
    auto& result = scopeValues.at(resultName);
54✔
219

220
    Register& operandRegister = operand.isStored() ? assignRegisterTo(operand) : operand.getAssignedRegister();
54✔
221
    Register& resultRegister = result.isStored() ? assignRegisterExcluding(result, operandRegister) : result.getAssignedRegister();
54✔
222
    assembly << instructionSet->mov(operandRegister, resultRegister, 0);
54✔
223
}
54✔
224

225
void StackMachine::procedureArgument(std::string argumentName) {
1,690✔
226
    auto argument = &scopeValues.at(argumentName);
1,690✔
227
    if (integerArguments.size() < registers->getIntegerArgumentRegisters().size()) {
1,690✔
228
        integerArguments.push_back(argument);
1,628✔
229
    } else {
230
        stackArguments.insert(stackArguments.begin(), argument);
62✔
231
    }
232
}
1,690✔
233

234
void StackMachine::callProcedure(std::string procedureName) {
754✔
235
    for (std::size_t i = 0; i < integerArguments.size(); ++i) {
2,382✔
236
        assignRegisterToSymbol(*registers->getIntegerArgumentRegisters()[i], *integerArguments[i]);
1,628✔
237
    }
238
    storeRegisterValue(registers->getRetrievalRegister());
754✔
239
    spillCallerSavedRegisters();
754✔
240
    int argumentOffset{0};
754✔
241
    // System V AMD64: RSP must be 16-byte aligned before call. Without stack args we are
242
    // aligned; each stack arg is 8 bytes, so an odd count needs 8 bytes of padding.
243
    if (stackArguments.size() % 2 == 1) {
754✔
244
        assembly << instructionSet->sub(registers->getStackPointer(), MACHINE_WORD_SIZE);
10✔
245
        argumentOffset += MACHINE_WORD_SIZE;
10✔
246
    }
247
    for (auto argument : stackArguments) {
816✔
248
        pushProcedureArgument(*argument, argumentOffset);
62✔
249
        argumentOffset += MACHINE_WORD_SIZE;
62✔
250
    }
251
    integerArguments.clear();
754✔
252
    stackArguments.clear();
754✔
253
    // AL must hold the number of vector registers used for variadic calls (System V AMD64).
254
    // This compiler only passes integer args, so set AL to 0 via xor rax, rax.
255
    auto& retrievalRegister = registers->getRetrievalRegister();
754✔
256
    assembly << instructionSet->xor_(retrievalRegister, retrievalRegister);
754✔
257
    assembly << instructionSet->call(procedureName);
754✔
258
    if (argumentOffset) {
754✔
259
        assembly << instructionSet->add(registers->getStackPointer(), argumentOffset);
16✔
260
    }
261
}
754✔
262

263
void StackMachine::pushProcedureArgument(Value& symbolToPush, int argumentOffset) {
62✔
264
    if (symbolToPush.isStored()) {
62✔
265
        Register& reg = get64BitRegister();
62✔
266
        assembly << instructionSet->mov(memoryBaseRegister(symbolToPush),
124✔
267
                                        symbolToPush.isFunctionArgument() ? memoryOffset(symbolToPush) : memoryOffset(symbolToPush) + argumentOffset, reg);
124✔
268
        assembly << instructionSet->push(reg);
62✔
269
    } else {
270
        assembly << instructionSet->push(symbolToPush.getAssignedRegister());
×
271
    }
272
}
62✔
273

274
void StackMachine::returnFromProcedure(std::string returnSymbolName) {
340✔
275
    if (!returnSymbolName.empty()) {
340✔
276
        Value& returnSymbol = scopeValues.at(returnSymbolName);
310✔
277
        if (returnSymbol.isStored()) {
310✔
278
            assembly << instructionSet->mov(memoryBaseRegister(returnSymbol), memoryOffset(returnSymbol), registers->getRetrievalRegister());
296✔
279
        } else if (&registers->getRetrievalRegister() != &returnSymbol.getAssignedRegister()) {
14✔
280
            assembly << instructionSet->mov(returnSymbol.getAssignedRegister(), registers->getRetrievalRegister());
10✔
281
        }
282
    }
283
    popCalleeSavedRegisters();
340✔
284
    assembly << instructionSet->leave();
340✔
285
    assembly << instructionSet->ret();
340✔
286
}
340✔
287

288
void StackMachine::retrieveProcedureReturnValue(std::string returnSymbolName) {
744✔
289
    Value& returnSymbol = scopeValues.at(returnSymbolName);
744✔
290
    assembly << instructionSet->mov(registers->getRetrievalRegister(), memoryBaseRegister(returnSymbol), memoryOffset(returnSymbol));
744✔
291
}
744✔
292

293
void StackMachine::xorCommand(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
6✔
294
    Value& leftOperand = scopeValues.at(leftOperandName);
6✔
295
    Value& rightOperand = scopeValues.at(rightOperandName);
6✔
296
    Register& resultRegister = get64BitRegister();
6✔
297

298
    if (leftOperand.isStored() && rightOperand.isStored()) {
6✔
299
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
4✔
300
        assembly << instructionSet->xor_(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
4✔
301
    } else if (leftOperand.isStored()) {
2✔
302
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
×
303
        assembly << instructionSet->xor_(rightOperand.getAssignedRegister(), resultRegister);
×
304
    } else if (rightOperand.isStored()) {
2✔
305
        assembly << instructionSet->mov(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
2✔
306
        assembly << instructionSet->xor_(leftOperand.getAssignedRegister(), resultRegister);
2✔
307
    } else {
308
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
×
309
        assembly << instructionSet->xor_(rightOperand.getAssignedRegister(), resultRegister);
×
310
    }
311
    resultRegister.assign(&scopeValues.at(resultName));
6✔
312
}
6✔
313

314
void StackMachine::orCommand(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
6✔
315
    Value& leftOperand = scopeValues.at(leftOperandName);
6✔
316
    Value& rightOperand = scopeValues.at(rightOperandName);
6✔
317
    Register& resultRegister = get64BitRegister();
6✔
318

319
    if (leftOperand.isStored() && rightOperand.isStored()) {
6✔
320
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
4✔
321
        assembly << instructionSet->or_(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
4✔
322
    } else if (leftOperand.isStored()) {
2✔
323
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
×
324
        assembly << instructionSet->or_(rightOperand.getAssignedRegister(), resultRegister);
×
325
    } else if (rightOperand.isStored()) {
2✔
326
        assembly << instructionSet->mov(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
2✔
327
        assembly << instructionSet->or_(leftOperand.getAssignedRegister(), resultRegister);
2✔
328
    } else {
329
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
×
330
        assembly << instructionSet->or_(rightOperand.getAssignedRegister(), resultRegister);
×
331
    }
332
    resultRegister.assign(&scopeValues.at(resultName));
6✔
333
}
6✔
334

335
void StackMachine::andCommand(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
6✔
336
    Value& leftOperand = scopeValues.at(leftOperandName);
6✔
337
    Value& rightOperand = scopeValues.at(rightOperandName);
6✔
338
    Register& resultRegister = get64BitRegister();
6✔
339

340
    if (leftOperand.isStored() && rightOperand.isStored()) {
6✔
341
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
4✔
342
        assembly << instructionSet->and_(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
4✔
343
    } else if (leftOperand.isStored()) {
2✔
344
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
×
345
        assembly << instructionSet->and_(rightOperand.getAssignedRegister(), resultRegister);
×
346
    } else if (rightOperand.isStored()) {
2✔
347
        assembly << instructionSet->mov(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
2✔
348
        assembly << instructionSet->and_(leftOperand.getAssignedRegister(), resultRegister);
2✔
349
    } else {
350
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
×
351
        assembly << instructionSet->and_(rightOperand.getAssignedRegister(), resultRegister);
×
352
    }
353
    resultRegister.assign(&scopeValues.at(resultName));
6✔
354
}
6✔
355

356
void StackMachine::add(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
64✔
357
    Value& leftOperand = scopeValues.at(leftOperandName);
64✔
358
    Value& rightOperand = scopeValues.at(rightOperandName);
64✔
359
    Register& resultRegister = get64BitRegister();
64✔
360

361
    if (leftOperand.isStored() && rightOperand.isStored()) {
64✔
362
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
36✔
363
        assembly << instructionSet->add(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
36✔
364
    } else if (leftOperand.isStored()) {
28✔
365
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
2✔
366
        assembly << instructionSet->add(rightOperand.getAssignedRegister(), resultRegister);
2✔
367
    } else if (rightOperand.isStored()) {
26✔
368
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
12✔
369
        assembly << instructionSet->add(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
12✔
370
    } else {
371
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
14✔
372
        assembly << instructionSet->add(rightOperand.getAssignedRegister(), resultRegister);
14✔
373
    }
374
    resultRegister.assign(&scopeValues.at(resultName));
64✔
375
}
64✔
376

377
void StackMachine::sub(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
28✔
378
    Value& leftOperand = scopeValues.at(leftOperandName);
28✔
379
    Value& rightOperand = scopeValues.at(rightOperandName);
28✔
380
    Register& resultRegister = get64BitRegister();
28✔
381

382
    if (leftOperand.isStored() && rightOperand.isStored()) {
28✔
383
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
18✔
384
        assembly << instructionSet->sub(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
18✔
385
    } else if (leftOperand.isStored()) {
10✔
386
        assembly << instructionSet->mov(memoryBaseRegister(leftOperand), memoryOffset(leftOperand), resultRegister);
2✔
387
        assembly << instructionSet->sub(rightOperand.getAssignedRegister(), resultRegister);
2✔
388
    } else if (rightOperand.isStored()) {
8✔
389
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
6✔
390
        assembly << instructionSet->sub(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), resultRegister);
6✔
391
    } else {
392
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
2✔
393
        assembly << instructionSet->sub(rightOperand.getAssignedRegister(), resultRegister);
2✔
394
    }
395
    resultRegister.assign(&scopeValues.at(resultName));
28✔
396
}
28✔
397

398
void StackMachine::mul(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
12✔
399
    Value& leftOperand = scopeValues.at(leftOperandName);
12✔
400
    Value& rightOperand = scopeValues.at(rightOperandName);
12✔
401
    Value& result = scopeValues.at(resultName);
12✔
402

403
    if (result.getType() != Type::INTEGRAL) {
12✔
404
        throw std::runtime_error{"multiplication of non integers is not implemented"};
×
405
    }
406

407
    Register& multiplicationRegister = registers->getMultiplicationRegister();
12✔
408
    assignRegisterToSymbol(multiplicationRegister, leftOperand);
12✔
409
    // imul writes RDX:RAX; spill RDX if it holds a live value (e.g. pointer for *p *= ...)
410
    storeRegisterValue(registers->getRemainderRegister());
12✔
411
    if (rightOperand.isStored()) {
12✔
412
        assembly << instructionSet->imul(memoryBaseRegister(rightOperand), memoryOffset(rightOperand));
12✔
413
    } else {
414
        assembly << instructionSet->imul(rightOperand.getAssignedRegister());
×
415
    }
416
    multiplicationRegister.assign(&result);
12✔
417
}
12✔
418

419
void StackMachine::div(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
6✔
420
    Value& leftOperand = scopeValues.at(leftOperandName);
6✔
421
    Value& rightOperand = scopeValues.at(rightOperandName);
6✔
422
    Value& result = scopeValues.at(resultName);
6✔
423

424
    if (result.getType() != Type::INTEGRAL) {
6✔
425
        throw std::runtime_error{"division of non integer types is not implemented"};
×
426
    }
427

428
    Register& multiplicationRegister = registers->getMultiplicationRegister();
6✔
429
    assignRegisterToSymbol(multiplicationRegister, leftOperand);
6✔
430
    storeRegisterValue(registers->getRemainderRegister());
6✔
431
    assembly << instructionSet->xor_(registers->getRemainderRegister(), registers->getRemainderRegister());
6✔
432
    if (rightOperand.isStored()) {
6✔
433
        assembly << instructionSet->idiv(memoryBaseRegister(rightOperand), memoryOffset(rightOperand));
6✔
434
    } else {
435
        assembly << instructionSet->idiv(rightOperand.getAssignedRegister());
×
436
    }
437
    multiplicationRegister.assign(&result);
6✔
438
}
6✔
439

440
void StackMachine::mod(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
6✔
441
    Value& leftOperand = scopeValues.at(leftOperandName);
6✔
442
    Value& rightOperand = scopeValues.at(rightOperandName);
6✔
443
    Value& result = scopeValues.at(resultName);
6✔
444

445
    if (result.getType() != Type::INTEGRAL) {
6✔
446
        throw std::runtime_error{"modular division of non integer types is not implemented"};
×
447
    }
448

449
    Register& multiplicationRegister = registers->getMultiplicationRegister();
6✔
450
    assignRegisterToSymbol(multiplicationRegister, leftOperand);
6✔
451
    storeRegisterValue(registers->getRemainderRegister());
6✔
452
    assembly << instructionSet->xor_(registers->getRemainderRegister(), registers->getRemainderRegister());
6✔
453
    if (rightOperand.isStored()) {
6✔
454
        assembly << instructionSet->idiv(memoryBaseRegister(rightOperand), memoryOffset(rightOperand));
6✔
455
    } else {
456
        assembly << instructionSet->idiv(rightOperand.getAssignedRegister());
×
457
    }
458
    registers->getRemainderRegister().assign(&result);
6✔
459
}
6✔
460

461
void StackMachine::inc(std::string operandName) {
38✔
462
    Value& operand = scopeValues.at(operandName);
38✔
463
    if (operand.isStored()) {
38✔
464
        assembly << instructionSet->inc(memoryBaseRegister(operand), memoryOffset(operand));
12✔
465
    } else {
466
        assembly << instructionSet->inc(operand.getAssignedRegister());
26✔
467
    }
468
}
38✔
469

470
void StackMachine::dec(std::string operandName) {
12✔
471
    Value& operand = scopeValues.at(operandName);
12✔
472
    if (operand.isStored()) {
12✔
473
        assembly << instructionSet->dec(memoryBaseRegister(operand), memoryOffset(operand));
4✔
474
    } else {
475
        assembly << instructionSet->dec(operand.getAssignedRegister());
8✔
476
    }
477
}
12✔
478

479
void StackMachine::shiftBy(std::string leftOperandName, std::string rightOperandName, std::string resultName,
28✔
480
        std::string (InstructionSet::*emitShift)(const Register&) const) {
481
    // Count must live in %cl (RCX) and be tracked so the value is not placed in RCX.
482
    Register& counterRegister = registers->getCounterRegister();
28✔
483
    storeRegisterValue(counterRegister);
28✔
484
    Value& rightOperand = scopeValues.at(rightOperandName);
28✔
485
    if (rightOperand.isStored()) {
28✔
486
        assembly << instructionSet->mov(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), counterRegister);
28✔
NEW
487
    } else if (&counterRegister != &rightOperand.getAssignedRegister()) {
×
NEW
488
        assembly << instructionSet->mov(rightOperand.getAssignedRegister(), counterRegister);
×
NEW
489
        storeRegisterValue(rightOperand.getAssignedRegister());
×
490
    }
491
    counterRegister.assign(&rightOperand);
28✔
492

493
    Value& leftOperand = scopeValues.at(leftOperandName);
28✔
494
    Register& resultRegister = get64BitRegisterExcluding(counterRegister);
28✔
495
    assignRegisterToSymbol(resultRegister, leftOperand);
28✔
496
    assembly << (instructionSet.get()->*emitShift)(resultRegister);
28✔
497
    Value& result = scopeValues.at(resultName);
28✔
498
    resultRegister.assign(&result);
28✔
499
}
28✔
500

501
void StackMachine::shl(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
14✔
502
    shiftBy(leftOperandName, rightOperandName, resultName, &InstructionSet::shl);
14✔
503
}
14✔
504

505
void StackMachine::shr(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
14✔
506
    shiftBy(leftOperandName, rightOperandName, resultName, &InstructionSet::shr);
14✔
507
}
14✔
508

509
void StackMachine::storeRegisterValue(Register& reg) {
19,536✔
510
    if (reg.containsUnstoredValue()) {
19,536✔
511
        assembly << instructionSet->mov(reg, memoryBaseRegister(*reg.getValue()), memoryOffset(*reg.getValue()));
1,076✔
512
        reg.free();
1,076✔
513
    }
514
}
19,536✔
515

516
void StackMachine::emptyGeneralPurposeRegisters() {
654✔
517
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
9,810✔
518
        reg->free();
9,156✔
519
    }
654✔
520
}
654✔
521

522
void StackMachine::pushCalleeSavedRegisters() { pushRegisters(registers->getCalleeSavedRegisters(), calleeSavedRegisters); }
332✔
523

524
void StackMachine::popCalleeSavedRegisters() { popRegisters(calleeSavedRegisters); }
340✔
525

526
void StackMachine::pushRegisters(std::vector<Register*> source, std::vector<Register*>& destination) {
332✔
527
    for (auto& reg : source) {
1,992✔
528
        pushRegister(*reg, destination);
1,660✔
529
    }
530
}
332✔
531

532
void StackMachine::popRegisters(std::vector<Register*> registers) {
340✔
533
    for (auto& reg : registers) {
2,030✔
534
        assembly << instructionSet->pop(*reg);
1,690✔
535
    }
536
}
340✔
537

538
void StackMachine::pushRegister(Register& reg, std::vector<Register*>& registers) {
1,660✔
539
    registers.insert(registers.begin(), &reg);
1,660✔
540
    assembly << instructionSet->push(reg);
1,660✔
541
}
1,660✔
542

543
void StackMachine::storeInMemory(Value& symbol) {
316✔
544
    if (!symbol.isStored()) {
316✔
545
        storeRegisterValue(symbol.getAssignedRegister());
44✔
546
    }
547
}
316✔
548

549
int StackMachine::memoryOffset(const Value& symbol) const {
6,880✔
550
    if (symbol.isFunctionArgument()) {
6,880✔
551
        return (symbol.getIndex() + 2) * MACHINE_WORD_SIZE;
50✔
552
    }
553
    return symbol.getIndex() * MACHINE_WORD_SIZE + calleeSavedRegisters.size() * MACHINE_WORD_SIZE;
6,830✔
554
}
555

556
const Register& StackMachine::memoryBaseRegister(const Value& symbol) const {
6,880✔
557
    if (symbol.isFunctionArgument()) {
6,880✔
558
        return registers->getBasePointer();
50✔
559
    }
560
    return registers->getStackPointer();
6,830✔
561
}
562

563
Register& StackMachine::get64BitRegister() {
900✔
564
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
1,310✔
565
        if (!reg->containsUnstoredValue()) {
1,310✔
566
            return *reg;
900✔
567
        }
568
    }
900✔
569
    Register& reg = **registers->getGeneralPurposeRegisters().begin();
×
570
    storeRegisterValue(reg);
×
571
    return reg;
×
572
}
573

574
Register& StackMachine::get64BitRegisterExcluding(Register& registerToExclude) {
192✔
575
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
736✔
576
        if (!reg->containsUnstoredValue()) {
732✔
577
            return *reg;
188✔
578
        }
579
    }
192✔
580
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
4✔
581
        if (reg != &registerToExclude) {
4✔
582
            storeRegisterValue(*reg);
4✔
583
            return *reg;
4✔
584
        }
585
    }
4✔
586
    throw std::runtime_error{"unable to get a free register"};
×
587
}
588

UNCOV
589
Register& StackMachine::getCounterRegister() {
×
UNCOV
590
    Register& counter = registers->getCounterRegister();
×
UNCOV
591
    storeRegisterValue(counter);
×
UNCOV
592
    return counter;
×
593
}
594

595
Register& StackMachine::assignRegisterTo(Value& symbol) {
330✔
596
    Register& reg = get64BitRegister();
330✔
597
    assembly << instructionSet->mov(memoryBaseRegister(symbol), memoryOffset(symbol), reg);
330✔
598
    reg.assign(&symbol);
330✔
599
    return reg;
330✔
600
}
601

602
Register& StackMachine::assignRegisterExcluding(Value& symbol, Register& registerToExclude) {
6✔
603
    Register& reg = get64BitRegisterExcluding(registerToExclude);
6✔
604
    assembly << instructionSet->mov(memoryBaseRegister(symbol), memoryOffset(symbol), reg);
6✔
605
    reg.assign(&symbol);
6✔
606
    return reg;
6✔
607
}
608

609
void StackMachine::setScope(std::vector<Value> variables) {
16✔
610
    for (auto& var : variables) {
64✔
611
        scopeValues.insert({var.getName(), var});
48✔
612
    }
613
}
16✔
614

615
} // namespace codegen
616

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc