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

rieske / trans / 28317922574

28 Jun 2026 09:29AM UTC coverage: 90.37% (+0.9%) from 89.474%
28317922574

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

15 of 21 new or added lines in 2 files covered. (71.43%)

4 existing lines in 1 file now uncovered.

4889 of 5410 relevant lines covered (90.37%)

220005.69 hits per line

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

92.47
/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::shl(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
14✔
480
    // Count must live in %cl (RCX) and be tracked so the value is not placed in RCX.
481
    Register& counterRegister = registers->getCounterRegister();
14✔
482
    storeRegisterValue(counterRegister);
14✔
483
    Value& rightOperand = scopeValues.at(rightOperandName);
14✔
484
    if (rightOperand.isStored()) {
14✔
485
        assembly << instructionSet->mov(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), counterRegister);
14✔
NEW
486
    } else if (&counterRegister != &rightOperand.getAssignedRegister()) {
×
NEW
487
        assembly << instructionSet->mov(rightOperand.getAssignedRegister(), counterRegister);
×
NEW
488
        storeRegisterValue(rightOperand.getAssignedRegister());
×
489
    }
490
    counterRegister.assign(&rightOperand);
14✔
491

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

500
void StackMachine::shr(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
14✔
501
    Register& counterRegister = registers->getCounterRegister();
14✔
502
    storeRegisterValue(counterRegister);
14✔
503
    Value& rightOperand = scopeValues.at(rightOperandName);
14✔
504
    if (rightOperand.isStored()) {
14✔
505
        assembly << instructionSet->mov(memoryBaseRegister(rightOperand), memoryOffset(rightOperand), counterRegister);
14✔
NEW
506
    } else if (&counterRegister != &rightOperand.getAssignedRegister()) {
×
NEW
507
        assembly << instructionSet->mov(rightOperand.getAssignedRegister(), counterRegister);
×
NEW
508
        storeRegisterValue(rightOperand.getAssignedRegister());
×
509
    }
510
    counterRegister.assign(&rightOperand);
14✔
511

512
    Value& leftOperand = scopeValues.at(leftOperandName);
14✔
513
    Register& resultRegister = get64BitRegisterExcluding(counterRegister);
14✔
514
    assignRegisterToSymbol(resultRegister, leftOperand);
14✔
515
    assembly << instructionSet->shr(resultRegister);
14✔
516
    Value& result = scopeValues.at(resultName);
14✔
517
    resultRegister.assign(&result);
14✔
518
}
14✔
519

520
void StackMachine::storeRegisterValue(Register& reg) {
19,536✔
521
    if (reg.containsUnstoredValue()) {
19,536✔
522
        assembly << instructionSet->mov(reg, memoryBaseRegister(*reg.getValue()), memoryOffset(*reg.getValue()));
1,076✔
523
        reg.free();
1,076✔
524
    }
525
}
19,536✔
526

527
void StackMachine::emptyGeneralPurposeRegisters() {
654✔
528
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
9,810✔
529
        reg->free();
9,156✔
530
    }
654✔
531
}
654✔
532

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

535
void StackMachine::popCalleeSavedRegisters() { popRegisters(calleeSavedRegisters); }
340✔
536

537
void StackMachine::pushRegisters(std::vector<Register*> source, std::vector<Register*>& destination) {
332✔
538
    for (auto& reg : source) {
1,992✔
539
        pushRegister(*reg, destination);
1,660✔
540
    }
541
}
332✔
542

543
void StackMachine::popRegisters(std::vector<Register*> registers) {
340✔
544
    for (auto& reg : registers) {
2,030✔
545
        assembly << instructionSet->pop(*reg);
1,690✔
546
    }
547
}
340✔
548

549
void StackMachine::pushRegister(Register& reg, std::vector<Register*>& registers) {
1,660✔
550
    registers.insert(registers.begin(), &reg);
1,660✔
551
    assembly << instructionSet->push(reg);
1,660✔
552
}
1,660✔
553

554
void StackMachine::storeInMemory(Value& symbol) {
316✔
555
    if (!symbol.isStored()) {
316✔
556
        storeRegisterValue(symbol.getAssignedRegister());
44✔
557
    }
558
}
316✔
559

560
int StackMachine::memoryOffset(const Value& symbol) const {
6,880✔
561
    if (symbol.isFunctionArgument()) {
6,880✔
562
        return (symbol.getIndex() + 2) * MACHINE_WORD_SIZE;
50✔
563
    }
564
    return symbol.getIndex() * MACHINE_WORD_SIZE + calleeSavedRegisters.size() * MACHINE_WORD_SIZE;
6,830✔
565
}
566

567
const Register& StackMachine::memoryBaseRegister(const Value& symbol) const {
6,880✔
568
    if (symbol.isFunctionArgument()) {
6,880✔
569
        return registers->getBasePointer();
50✔
570
    }
571
    return registers->getStackPointer();
6,830✔
572
}
573

574
Register& StackMachine::get64BitRegister() {
900✔
575
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
1,310✔
576
        if (!reg->containsUnstoredValue()) {
1,310✔
577
            return *reg;
900✔
578
        }
579
    }
900✔
580
    Register& reg = **registers->getGeneralPurposeRegisters().begin();
×
581
    storeRegisterValue(reg);
×
582
    return reg;
×
583
}
584

585
Register& StackMachine::get64BitRegisterExcluding(Register& registerToExclude) {
192✔
586
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
736✔
587
        if (!reg->containsUnstoredValue()) {
732✔
588
            return *reg;
188✔
589
        }
590
    }
192✔
591
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
4✔
592
        if (reg != &registerToExclude) {
4✔
593
            storeRegisterValue(*reg);
4✔
594
            return *reg;
4✔
595
        }
596
    }
4✔
597
    throw std::runtime_error{"unable to get a free register"};
×
598
}
599

UNCOV
600
Register& StackMachine::getCounterRegister() {
×
UNCOV
601
    Register& counter = registers->getCounterRegister();
×
UNCOV
602
    storeRegisterValue(counter);
×
UNCOV
603
    return counter;
×
604
}
605

606
Register& StackMachine::assignRegisterTo(Value& symbol) {
330✔
607
    Register& reg = get64BitRegister();
330✔
608
    assembly << instructionSet->mov(memoryBaseRegister(symbol), memoryOffset(symbol), reg);
330✔
609
    reg.assign(&symbol);
330✔
610
    return reg;
330✔
611
}
612

613
Register& StackMachine::assignRegisterExcluding(Value& symbol, Register& registerToExclude) {
6✔
614
    Register& reg = get64BitRegisterExcluding(registerToExclude);
6✔
615
    assembly << instructionSet->mov(memoryBaseRegister(symbol), memoryOffset(symbol), reg);
6✔
616
    reg.assign(&symbol);
6✔
617
    return reg;
6✔
618
}
619

620
void StackMachine::setScope(std::vector<Value> variables) {
16✔
621
    for (auto& var : variables) {
64✔
622
        scopeValues.insert({var.getName(), var});
48✔
623
    }
624
}
16✔
625

626
} // namespace codegen
627

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