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

rieske / trans / 28379938466

29 Jun 2026 02:35PM UTC coverage: 90.44% (-0.2%) from 90.635%
28379938466

push

github

web-flow
Merge pull request #41 from rieske/feature/file-scope-globals

Add file-scope global variables

300 of 358 new or added lines in 19 files covered. (83.8%)

8 existing lines in 2 files now uncovered.

5052 of 5586 relevant lines covered (90.44%)

242657.94 hits per line

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

95.27
/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) :
380✔
15
        assembly{ostream},
380✔
16
        instructionSet{std::move(instructionSet)},
380✔
17
        registers{std::move(registers)} {}
760✔
18

19
void StackMachine::generatePreamble(const std::map<std::string, std::string>& constants,
348✔
20
        const std::vector<GlobalVariable>& globalVariables) {
21
    assembly.raw(instructionSet->preamble(constants, globalVariables));
348✔
22
    // Register every global once with real size/type; resolve() falls back here for non-frame names.
23
    for (const auto& global : globalVariables) {
432✔
24
        globals.emplace(global.name, global.toValue());
84✔
25
    }
26
}
348✔
27

28
void StackMachine::startProcedure(std::string procedureName, std::vector<Value> values, std::vector<Value> arguments) {
440✔
29

30
    emptyGeneralPurposeRegisters();
440✔
31
    assembly.label(instructionSet->label(procedureName));
440✔
32
    assembly << instructionSet->push(registers->getBasePointer());
440✔
33
    assembly << instructionSet->mov(registers->getStackPointer(), registers->getBasePointer());
440✔
34

35
    for (auto& value : values) {
4,646✔
36
        scopeValues.insert({value.getName(), value});
4,206✔
37
    }
38
    std::size_t integerArgumentRegisterIndex{0};
440✔
39
    std::size_t localIndex{scopeValues.size()};
440✔
40
    int argumentIndex{0};
440✔
41
    for (auto& argument : arguments) {
604✔
42
        if (argument.getType() == Type::INTEGRAL && integerArgumentRegisterIndex < registers->getIntegerArgumentRegisters().size()) {
164✔
43
            Value registerArgument{argument.getName(), static_cast<int>(localIndex), argument.getType(), argument.getSizeInBytes()};
116✔
44
            scopeValues.insert({argument.getName(), registerArgument});
116✔
45
            registers->getIntegerArgumentRegisters()[integerArgumentRegisterIndex]->assign(&resolve(argument.getName()));
116✔
46
            ++integerArgumentRegisterIndex;
116✔
47
            ++localIndex;
116✔
48
        } else {
116✔
49
            Value stackArgument{argument.getName(), argumentIndex, argument.getType(), argument.getSizeInBytes(), true};
48✔
50
            scopeValues.insert({argument.getName(), stackArgument});
48✔
51
            ++argumentIndex;
48✔
52
        }
48✔
53
    }
54
    int savedRegistersStack = registers->getCalleeSavedRegisters().size() * MACHINE_WORD_SIZE;
440✔
55
    localVariableStackSize = (scopeValues.size() - argumentIndex) * MACHINE_WORD_SIZE;
440✔
56
    int stackSize = savedRegistersStack + localVariableStackSize;
440✔
57
    if (stackSize % STACK_ALIGNMENT) {
440✔
58
        assembly << instructionSet->sub(registers->getStackPointer(), localVariableStackSize + MACHINE_WORD_SIZE);
206✔
59
    } else {
60
        assembly << instructionSet->sub(registers->getStackPointer(), localVariableStackSize);
234✔
61
    }
62

63
    pushCalleeSavedRegisters();
440✔
64
}
440✔
65

66
void StackMachine::endProcedure() {
430✔
67
    emptyGeneralPurposeRegisters();
430✔
68
    scopeValues.clear();
430✔
69
    calleeSavedRegisters.clear();
430✔
70
}
430✔
71

72
void StackMachine::label(std::string name) {
584✔
73
    spillGeneralPurposeRegisters();
584✔
74
    assembly.label(instructionSet->label(name));
584✔
75
}
584✔
76

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

104
void StackMachine::spillGeneralPurposeRegisters() {
858✔
105
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
12,870✔
106
        storeRegisterValue(*reg);
12,012✔
107
    }
858✔
108
}
858✔
109

110
void StackMachine::spillCallerSavedRegisters() {
880✔
111
    for (auto& reg : registers->getCallerSavedRegisters()) {
7,920✔
112
        storeRegisterValue(*reg);
7,040✔
113
    }
880✔
114
}
880✔
115

116
void StackMachine::emitLoad(Value& symbol, Register& dest) {
2,634✔
117
    assembly << instructionSet->mov(memoryOperand(symbol), dest);
2,634✔
118
}
2,634✔
119

120
void StackMachine::emitStore(Register& source, Value& symbol) {
2,248✔
121
    assembly << instructionSet->mov(source, memoryOperand(symbol));
2,248✔
122
}
2,248✔
123

124
// Bind a freshly computed result to its destination symbol. A global is never register-resident,
125
// so write it straight to [rel name]; a local/temp stays register-resident for lazy write-back.
126
void StackMachine::bindResult(Register& reg, Value& result) {
704✔
127
    if (result.isGlobal()) {
704✔
128
        emitStore(reg, result);
22✔
129
    } else {
130
        reg.assign(&result);
682✔
131
    }
132
}
704✔
133

134
void StackMachine::assignRegisterToSymbol(Register& reg, Value& symbol) {
1,936✔
135
    if (symbol.isStored()) {
1,936✔
136
        storeRegisterValue(reg);
1,558✔
137
        emitLoad(symbol, reg);
1,558✔
138
    } else if (&reg != &symbol.getAssignedRegister()) {
378✔
139
        storeRegisterValue(reg);
370✔
140
        Register& valueRegister = symbol.getAssignedRegister();
370✔
141
        storeRegisterValue(valueRegister);
370✔
142
        assembly << instructionSet->mov(valueRegister, reg);
370✔
143
    }
144
}
1,936✔
145

146
void StackMachine::compare(std::string leftSymbolName, std::string rightSymbolName) {
178✔
147
    auto& leftSymbol = resolve(leftSymbolName);
178✔
148
    auto& rightSymbol = resolve(rightSymbolName);
178✔
149

150
    if (leftSymbol.isStored() && rightSymbol.isStored()) {
178✔
151
        Register& rightSymbolRegister = assignRegisterTo(rightSymbol);
122✔
152
        assembly << instructionSet->cmp(memoryOperand(leftSymbol), rightSymbolRegister);
122✔
153
    } else if (leftSymbol.isStored()) {
56✔
154
        assembly << instructionSet->cmp(memoryOperand(leftSymbol), rightSymbol.getAssignedRegister());
16✔
155
    } else if (rightSymbol.isStored()) {
40✔
156
        assembly << instructionSet->cmp(leftSymbol.getAssignedRegister(), memoryOperand(rightSymbol));
20✔
157
    } else {
158
        assembly << instructionSet->cmp(leftSymbol.getAssignedRegister(), rightSymbol.getAssignedRegister());
20✔
159
    }
160
}
178✔
161

162
void StackMachine::zeroCompare(std::string symbolName) {
140✔
163
    auto& symbol = resolve(symbolName);
140✔
164
    if (symbol.isStored()) {
140✔
165
        assembly << instructionSet->cmp(memoryOperand(symbol), 0);
130✔
166
    } else {
167
        assembly << instructionSet->cmp(symbol.getAssignedRegister(), 0);
10✔
168
    }
169
}
140✔
170

171
void StackMachine::addressOf(std::string operandName, std::string resultName) {
318✔
172
    auto& operand = resolve(operandName);
318✔
173
    storeInMemory(operand);
318✔
174
    Register& resultRegister = get64BitRegister();
318✔
175
    assembly << instructionSet->lea(memoryOperand(operand), resultRegister);
318✔
176
    bindResult(resultRegister, resolve(resultName));
318✔
177
}
318✔
178

179
void StackMachine::dereference(std::string operandName, std::string lvalueName, std::string resultName) {
78✔
180
    auto& operand = resolve(operandName);
78✔
181
    if (operand.isStored()) {
78✔
182
        assignRegisterTo(operand);
48✔
183
    }
184
    Register& resultRegister = get64BitRegisterExcluding(operand.getAssignedRegister());
78✔
185
    assembly << instructionSet->mov(MemoryOperand::at(operand.getAssignedRegister(), 0), resultRegister);
78✔
186
    bindResult(resultRegister, resolve(resultName));
78✔
187

188
    Register& lvalueRegister = get64BitRegisterExcluding(operand.getAssignedRegister());
78✔
189
    assembly << instructionSet->mov(operand.getAssignedRegister(), lvalueRegister);
78✔
190
    lvalueRegister.assign(&resolve(lvalueName));
78✔
191
}
78✔
192

193
void StackMachine::unaryMinus(std::string operandName, std::string resultName) {
88✔
194
    auto& operand = resolve(operandName);
88✔
195
    if (operand.isStored()) {
88✔
196
        Register& resultRegister = get64BitRegister();
82✔
197
        emitLoad(operand, resultRegister);
82✔
198
        assembly << instructionSet->neg(resultRegister);
82✔
199
        bindResult(resultRegister, resolve(resultName));
82✔
200
    } else {
201
        Register& operandRegister = operand.getAssignedRegister();
6✔
202
        Register& resultRegister = get64BitRegisterExcluding(operand.getAssignedRegister());
6✔
203
        assembly << instructionSet->mov(operandRegister, resultRegister);
6✔
204
        assembly << instructionSet->neg(resultRegister);
6✔
205
        bindResult(resultRegister, resolve(resultName));
6✔
206
    }
207
}
88✔
208

209
void StackMachine::assign(std::string operandName, std::string resultName) {
390✔
210
    auto& operand = resolve(operandName);
390✔
211
    auto& result = resolve(resultName);
390✔
212

213
    if (operand.isStored() && result.isStored()) {
390✔
214
        Register& reg = get64BitRegister();
264✔
215
        emitLoad(operand, reg);
264✔
216
        emitStore(reg, result);
264✔
217
    } else if (operand.isStored()) {
126✔
NEW
218
        emitLoad(operand, result.getAssignedRegister());
×
219
    } else if (result.isStored()) {
126✔
220
        emitStore(operand.getAssignedRegister(), result);
126✔
221
    } else {
222
        assembly << instructionSet->mov(operand.getAssignedRegister(), result.getAssignedRegister());
×
223
    }
224
}
390✔
225

226
void StackMachine::assignConstant(std::string constant, std::string resultName) {
2,332✔
227
    auto& result = resolve(resultName);
2,332✔
228
    if (result.isStored()) {
2,332✔
229
        assembly << instructionSet->mov(constant, memoryOperand(result));
2,332✔
230
    } else {
NEW
231
        assembly << instructionSet->mov(constant, result.getAssignedRegister());
×
232
    }
233
}
2,332✔
234

235
void StackMachine::lvalueAssign(std::string operandName, std::string resultName) {
56✔
236
    auto& operand = resolve(operandName);
56✔
237
    auto& result = resolve(resultName);
56✔
238

239
    Register& operandRegister = operand.isStored() ? assignRegisterTo(operand) : operand.getAssignedRegister();
56✔
240
    Register& resultRegister = result.isStored() ? assignRegisterExcluding(result, operandRegister) : result.getAssignedRegister();
56✔
241
    assembly << instructionSet->mov(operandRegister, MemoryOperand::at(resultRegister, 0));
56✔
242
}
56✔
243

244
void StackMachine::procedureArgument(std::string argumentName) {
1,924✔
245
    auto argument = &resolve(argumentName);
1,924✔
246
    if (integerArguments.size() < registers->getIntegerArgumentRegisters().size()) {
1,924✔
247
        integerArguments.push_back(argument);
1,862✔
248
    } else {
249
        stackArguments.insert(stackArguments.begin(), argument);
62✔
250
    }
251
}
1,924✔
252

253
void StackMachine::callProcedure(std::string procedureName) {
880✔
254
    for (std::size_t i = 0; i < integerArguments.size(); ++i) {
2,742✔
255
        assignRegisterToSymbol(*registers->getIntegerArgumentRegisters()[i], *integerArguments[i]);
1,862✔
256
    }
257
    storeRegisterValue(registers->getRetrievalRegister());
880✔
258
    spillCallerSavedRegisters();
880✔
259
    int argumentOffset{0};
880✔
260
    // System V AMD64: RSP must be 16-byte aligned before call. Without stack args we are
261
    // aligned; each stack arg is 8 bytes, so an odd count needs 8 bytes of padding.
262
    if (stackArguments.size() % 2 == 1) {
880✔
263
        assembly << instructionSet->sub(registers->getStackPointer(), MACHINE_WORD_SIZE);
10✔
264
        argumentOffset += MACHINE_WORD_SIZE;
10✔
265
    }
266
    for (auto argument : stackArguments) {
942✔
267
        pushProcedureArgument(*argument, argumentOffset);
62✔
268
        argumentOffset += MACHINE_WORD_SIZE;
62✔
269
    }
270
    integerArguments.clear();
880✔
271
    stackArguments.clear();
880✔
272
    // AL must hold the number of vector registers used for variadic calls (System V AMD64).
273
    // This compiler only passes integer args, so set AL to 0 via xor rax, rax.
274
    auto& retrievalRegister = registers->getRetrievalRegister();
880✔
275
    assembly << instructionSet->xor_(retrievalRegister, retrievalRegister);
880✔
276
    assembly << instructionSet->call(procedureName);
880✔
277
    if (argumentOffset) {
880✔
278
        assembly << instructionSet->add(registers->getStackPointer(), argumentOffset);
16✔
279
    }
280
}
880✔
281

282
void StackMachine::pushProcedureArgument(Value& symbolToPush, int argumentOffset) {
62✔
283
    if (symbolToPush.isStored()) {
62✔
284
        Register& reg = get64BitRegister();
62✔
285
        // rsp moves as stack args are pushed, so a non-argument stack operand needs the running offset.
286
        int extraOffset = symbolToPush.isFunctionArgument() ? 0 : argumentOffset;
62✔
287
        assembly << instructionSet->mov(memoryOperand(symbolToPush, extraOffset), reg);
62✔
288
        assembly << instructionSet->push(reg);
62✔
289
    } else {
290
        assembly << instructionSet->push(symbolToPush.getAssignedRegister());
×
291
    }
292
}
62✔
293

294
void StackMachine::returnFromProcedure(std::string returnSymbolName) {
456✔
295
    if (!returnSymbolName.empty()) {
456✔
296
        Value& returnSymbol = resolve(returnSymbolName);
418✔
297
        if (returnSymbol.isStored()) {
418✔
298
            emitLoad(returnSymbol, registers->getRetrievalRegister());
404✔
299
        } else if (&registers->getRetrievalRegister() != &returnSymbol.getAssignedRegister()) {
14✔
300
            assembly << instructionSet->mov(returnSymbol.getAssignedRegister(), registers->getRetrievalRegister());
10✔
301
        }
302
    }
303
    popCalleeSavedRegisters();
456✔
304
    assembly << instructionSet->leave();
456✔
305
    assembly << instructionSet->ret();
456✔
306
}
456✔
307

308
void StackMachine::retrieveProcedureReturnValue(std::string returnSymbolName) {
870✔
309
    Value& returnSymbol = resolve(returnSymbolName);
870✔
310
    emitStore(registers->getRetrievalRegister(), returnSymbol);
870✔
311
}
870✔
312

313
void StackMachine::xorCommand(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
314
    Value& leftOperand = resolve(leftOperandName);
10✔
315
    Value& rightOperand = resolve(rightOperandName);
10✔
316
    Register& resultRegister = get64BitRegister();
10✔
317

318
    if (leftOperand.isStored()) {
10✔
319
        emitLoad(leftOperand, resultRegister);
8✔
320
    } else {
321
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
2✔
322
    }
323
    if (rightOperand.isStored()) {
10✔
324
        assembly << instructionSet->xor_(memoryOperand(rightOperand), resultRegister);
10✔
325
    } else {
UNCOV
326
        assembly << instructionSet->xor_(rightOperand.getAssignedRegister(), resultRegister);
×
327
    }
328
    bindResult(resultRegister, resolve(resultName));
10✔
329
}
10✔
330

331
void StackMachine::orCommand(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
332
    Value& leftOperand = resolve(leftOperandName);
10✔
333
    Value& rightOperand = resolve(rightOperandName);
10✔
334
    Register& resultRegister = get64BitRegister();
10✔
335

336
    if (leftOperand.isStored()) {
10✔
337
        emitLoad(leftOperand, resultRegister);
8✔
338
    } else {
339
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
2✔
340
    }
341
    if (rightOperand.isStored()) {
10✔
342
        assembly << instructionSet->or_(memoryOperand(rightOperand), resultRegister);
10✔
343
    } else {
UNCOV
344
        assembly << instructionSet->or_(rightOperand.getAssignedRegister(), resultRegister);
×
345
    }
346
    bindResult(resultRegister, resolve(resultName));
10✔
347
}
10✔
348

349
void StackMachine::andCommand(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
350
    Value& leftOperand = resolve(leftOperandName);
10✔
351
    Value& rightOperand = resolve(rightOperandName);
10✔
352
    Register& resultRegister = get64BitRegister();
10✔
353

354
    if (leftOperand.isStored()) {
10✔
355
        emitLoad(leftOperand, resultRegister);
8✔
356
    } else {
357
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
2✔
358
    }
359
    if (rightOperand.isStored()) {
10✔
360
        assembly << instructionSet->and_(memoryOperand(rightOperand), resultRegister);
10✔
361
    } else {
UNCOV
362
        assembly << instructionSet->and_(rightOperand.getAssignedRegister(), resultRegister);
×
363
    }
364
    bindResult(resultRegister, resolve(resultName));
10✔
365
}
10✔
366

367
void StackMachine::add(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
86✔
368
    Value& leftOperand = resolve(leftOperandName);
86✔
369
    Value& rightOperand = resolve(rightOperandName);
86✔
370
    Register& resultRegister = get64BitRegister();
86✔
371

372
    if (leftOperand.isStored()) {
86✔
373
        emitLoad(leftOperand, resultRegister);
62✔
374
    } else {
375
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
24✔
376
    }
377
    if (rightOperand.isStored()) {
86✔
378
        assembly << instructionSet->add(memoryOperand(rightOperand), resultRegister);
70✔
379
    } else {
380
        assembly << instructionSet->add(rightOperand.getAssignedRegister(), resultRegister);
16✔
381
    }
382
    bindResult(resultRegister, resolve(resultName));
86✔
383
}
86✔
384

385
void StackMachine::sub(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
30✔
386
    Value& leftOperand = resolve(leftOperandName);
30✔
387
    Value& rightOperand = resolve(rightOperandName);
30✔
388
    Register& resultRegister = get64BitRegister();
30✔
389

390
    if (leftOperand.isStored()) {
30✔
391
        emitLoad(leftOperand, resultRegister);
22✔
392
    } else {
393
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
8✔
394
    }
395
    if (rightOperand.isStored()) {
30✔
396
        assembly << instructionSet->sub(memoryOperand(rightOperand), resultRegister);
26✔
397
    } else {
398
        assembly << instructionSet->sub(rightOperand.getAssignedRegister(), resultRegister);
4✔
399
    }
400
    bindResult(resultRegister, resolve(resultName));
30✔
401
}
30✔
402

403
void StackMachine::mul(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
16✔
404
    Value& leftOperand = resolve(leftOperandName);
16✔
405
    Value& rightOperand = resolve(rightOperandName);
16✔
406
    Value& result = resolve(resultName);
16✔
407

408
    if (result.getType() != Type::INTEGRAL) {
16✔
409
        throw std::runtime_error{"multiplication of non integers is not implemented"};
×
410
    }
411

412
    Register& multiplicationRegister = registers->getMultiplicationRegister();
16✔
413
    assignRegisterToSymbol(multiplicationRegister, leftOperand);
16✔
414
    // imul writes RDX:RAX; spill RDX if it holds a live value (e.g. pointer for *p *= ...)
415
    storeRegisterValue(registers->getRemainderRegister());
16✔
416
    if (rightOperand.isStored()) {
16✔
417
        assembly << instructionSet->imul(memoryOperand(rightOperand));
16✔
418
    } else {
419
        assembly << instructionSet->imul(rightOperand.getAssignedRegister());
×
420
    }
421
    bindResult(multiplicationRegister, result);
16✔
422
}
16✔
423

424
void StackMachine::div(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
425
    Value& leftOperand = resolve(leftOperandName);
10✔
426
    Value& rightOperand = resolve(rightOperandName);
10✔
427
    Value& result = resolve(resultName);
10✔
428

429
    if (result.getType() != Type::INTEGRAL) {
10✔
430
        throw std::runtime_error{"division of non integer types is not implemented"};
×
431
    }
432

433
    Register& multiplicationRegister = registers->getMultiplicationRegister();
10✔
434
    assignRegisterToSymbol(multiplicationRegister, leftOperand);
10✔
435
    storeRegisterValue(registers->getRemainderRegister());
10✔
436
    assembly << instructionSet->xor_(registers->getRemainderRegister(), registers->getRemainderRegister());
10✔
437
    if (rightOperand.isStored()) {
10✔
438
        assembly << instructionSet->idiv(memoryOperand(rightOperand));
10✔
439
    } else {
440
        assembly << instructionSet->idiv(rightOperand.getAssignedRegister());
×
441
    }
442
    bindResult(multiplicationRegister, result);
10✔
443
}
10✔
444

445
void StackMachine::mod(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
446
    Value& leftOperand = resolve(leftOperandName);
10✔
447
    Value& rightOperand = resolve(rightOperandName);
10✔
448
    Value& result = resolve(resultName);
10✔
449

450
    if (result.getType() != Type::INTEGRAL) {
10✔
451
        throw std::runtime_error{"modular division of non integer types is not implemented"};
×
452
    }
453

454
    Register& multiplicationRegister = registers->getMultiplicationRegister();
10✔
455
    assignRegisterToSymbol(multiplicationRegister, leftOperand);
10✔
456
    storeRegisterValue(registers->getRemainderRegister());
10✔
457
    assembly << instructionSet->xor_(registers->getRemainderRegister(), registers->getRemainderRegister());
10✔
458
    if (rightOperand.isStored()) {
10✔
459
        assembly << instructionSet->idiv(memoryOperand(rightOperand));
10✔
460
    } else {
461
        assembly << instructionSet->idiv(rightOperand.getAssignedRegister());
×
462
    }
463
    bindResult(registers->getRemainderRegister(), result);
10✔
464
}
10✔
465

466
void StackMachine::inc(std::string operandName) {
40✔
467
    Value& operand = resolve(operandName);
40✔
468
    if (operand.isStored()) {
40✔
469
        assembly << instructionSet->inc(memoryOperand(operand));
12✔
470
    } else {
471
        assembly << instructionSet->inc(operand.getAssignedRegister());
28✔
472
    }
473
}
40✔
474

475
void StackMachine::dec(std::string operandName) {
12✔
476
    Value& operand = resolve(operandName);
12✔
477
    if (operand.isStored()) {
12✔
478
        assembly << instructionSet->dec(memoryOperand(operand));
4✔
479
    } else {
480
        assembly << instructionSet->dec(operand.getAssignedRegister());
8✔
481
    }
482
}
12✔
483

484
void StackMachine::shiftBy(std::string leftOperandName, std::string rightOperandName, std::string resultName,
38✔
485
        std::string (InstructionSet::*emitShift)(const Register&) const) {
486
    // Count must live in %cl (RCX) and be tracked so the value is not placed in RCX.
487
    Register& counterRegister = getCounterRegister();
38✔
488
    Value& rightOperand = resolve(rightOperandName);
38✔
489
    if (rightOperand.isStored()) {
38✔
490
        emitLoad(rightOperand, counterRegister);
38✔
491
    } else if (&counterRegister != &rightOperand.getAssignedRegister()) {
×
492
        assembly << instructionSet->mov(rightOperand.getAssignedRegister(), counterRegister);
×
493
        storeRegisterValue(rightOperand.getAssignedRegister());
×
494
    }
495
    counterRegister.assign(&rightOperand);
38✔
496

497
    Value& leftOperand = resolve(leftOperandName);
38✔
498
    Register& resultRegister = get64BitRegisterExcluding(counterRegister);
38✔
499
    assignRegisterToSymbol(resultRegister, leftOperand);
38✔
500
    assembly << (instructionSet.get()->*emitShift)(resultRegister);
38✔
501
    Value& result = resolve(resultName);
38✔
502
    bindResult(resultRegister, result);
38✔
503
}
38✔
504

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

509
void StackMachine::shr(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
18✔
510
    shiftBy(leftOperandName, rightOperandName, resultName, &InstructionSet::shr);
18✔
511
}
18✔
512

513
void StackMachine::storeRegisterValue(Register& reg) {
22,308✔
514
    if (reg.containsUnstoredValue()) {
22,308✔
515
        emitStore(reg, *reg.getValue());
966✔
516
        reg.free();
966✔
517
    }
518
}
22,308✔
519

520
void StackMachine::emptyGeneralPurposeRegisters() {
870✔
521
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
13,050✔
522
        reg->free();
12,180✔
523
    }
870✔
524
}
870✔
525

526
void StackMachine::pushCalleeSavedRegisters() { pushRegisters(registers->getCalleeSavedRegisters(), calleeSavedRegisters); }
440✔
527

528
void StackMachine::popCalleeSavedRegisters() { popRegisters(calleeSavedRegisters); }
456✔
529

530
void StackMachine::pushRegisters(std::vector<Register*> source, std::vector<Register*>& destination) {
440✔
531
    for (auto& reg : source) {
2,640✔
532
        pushRegister(*reg, destination);
2,200✔
533
    }
534
}
440✔
535

536
void StackMachine::popRegisters(std::vector<Register*> registers) {
456✔
537
    for (auto& reg : registers) {
2,726✔
538
        assembly << instructionSet->pop(*reg);
2,270✔
539
    }
540
}
456✔
541

542
void StackMachine::pushRegister(Register& reg, std::vector<Register*>& registers) {
2,200✔
543
    registers.insert(registers.begin(), &reg);
2,200✔
544
    assembly << instructionSet->push(reg);
2,200✔
545
}
2,200✔
546

547
void StackMachine::storeInMemory(Value& symbol) {
318✔
548
    if (!symbol.isStored()) {
318✔
UNCOV
549
        storeRegisterValue(symbol.getAssignedRegister());
×
550
    }
551
}
318✔
552

553
int StackMachine::memoryOffset(const Value& symbol) const {
7,858✔
554
    if (symbol.isFunctionArgument()) {
7,858✔
555
        return (symbol.getIndex() + 2) * MACHINE_WORD_SIZE;
50✔
556
    }
557
    return symbol.getIndex() * MACHINE_WORD_SIZE + calleeSavedRegisters.size() * MACHINE_WORD_SIZE;
7,808✔
558
}
559

560
MemoryOperand StackMachine::memoryOperand(const Value& symbol, int extraOffset) const {
8,060✔
561
    if (symbol.isGlobal()) {
8,060✔
562
        return MemoryOperand::global(symbol.getName());
202✔
563
    }
564
    // Arguments are addressed off rbp, locals off rsp.
565
    const Register& base = symbol.isFunctionArgument() ? registers->getBasePointer() : registers->getStackPointer();
7,858✔
566
    return MemoryOperand::at(base, memoryOffset(symbol) + extraOffset);
7,858✔
567
}
568

569
Register& StackMachine::get64BitRegister() {
1,046✔
570
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
1,394✔
571
        if (!reg->containsUnstoredValue()) {
1,394✔
572
            return *reg;
1,046✔
573
        }
574
    }
1,046✔
575
    Register& reg = **registers->getGeneralPurposeRegisters().begin();
×
576
    storeRegisterValue(reg);
×
577
    return reg;
×
578
}
579

580
Register& StackMachine::get64BitRegisterExcluding(Register& registerToExclude) {
206✔
581
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
746✔
582
        if (reg != &registerToExclude && !reg->containsUnstoredValue()) {
742✔
583
            return *reg;
202✔
584
        }
585
    }
206✔
586
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
4✔
587
        if (reg != &registerToExclude) {
4✔
588
            storeRegisterValue(*reg);
4✔
589
            return *reg;
4✔
590
        }
591
    }
4✔
592
    throw std::runtime_error{"unable to get a free register"};
×
593
}
594

595
Register& StackMachine::getCounterRegister() {
38✔
596
    Register& counter = registers->getCounterRegister();
38✔
597
    storeRegisterValue(counter);
38✔
598
    return counter;
38✔
599
}
600

601
Register& StackMachine::assignRegisterTo(Value& symbol) {
174✔
602
    Register& reg = get64BitRegister();
174✔
603
    emitLoad(symbol, reg);
174✔
604
    reg.assign(&symbol);
174✔
605
    return reg;
174✔
606
}
607

608
Register& StackMachine::assignRegisterExcluding(Value& symbol, Register& registerToExclude) {
6✔
609
    Register& reg = get64BitRegisterExcluding(registerToExclude);
6✔
610
    emitLoad(symbol, reg);
6✔
611
    reg.assign(&symbol);
6✔
612
    return reg;
6✔
613
}
614

615
void StackMachine::setScope(std::vector<Value> variables) {
16✔
616
    for (auto& var : variables) {
64✔
617
        scopeValues.insert({var.getName(), var});
48✔
618
    }
619
}
16✔
620

621
Value& StackMachine::resolve(const std::string& name) {
8,806✔
622
    auto local = scopeValues.find(name);
8,806✔
623
    if (local != scopeValues.end()) {
8,806✔
624
        return local->second;
8,604✔
625
    }
626
    return globals.at(name);
202✔
627
}
628

629
} // namespace codegen
630

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