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

rieske / trans / 28381660498

29 Jun 2026 02:37PM UTC coverage: 90.47% (+0.03%) from 90.44%
28381660498

Pull #42

github

rieske
Register object homes in StackMachine (Address phase 2)

Track globalHomes and per-procedure frameHomes. addressOf prefers
registered homes over Value storage flags; bindResult commits when
isGlobalHome. Frame homes are recorded after callee-saved pushes so
offsets match memoryOffset. Value remains the register-cache entity.
Pull Request #42: Codegen Address object homes (phase B)

51 of 54 new or added lines in 2 files covered. (94.44%)

5098 of 5635 relevant lines covered (90.47%)

240581.09 hits per line

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

94.98
/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
    // Global object homes + Value caches for resolve() (register assign still needs a Value).
23
    for (const auto& global : globalVariables) {
432✔
24
        globalHomes.emplace(global.name, Address::globalLabel(global.name, global.sizeInBytes));
84✔
25
        globals.emplace(global.name, global.toValue());
84✔
26
    }
27
}
348✔
28

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

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

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

65
    pushCalleeSavedRegisters();
440✔
66
    // Frame offsets include callee-saved space; register homes only after that is known.
67
    for (const auto& entry : scopeValues) {
4,810✔
68
        registerFrameHome(entry.second);
4,370✔
69
    }
70
}
440✔
71

72
void StackMachine::endProcedure() {
430✔
73
    emptyGeneralPurposeRegisters();
430✔
74
    scopeValues.clear();
430✔
75
    frameHomes.clear();
430✔
76
    calleeSavedRegisters.clear();
430✔
77
}
430✔
78

79
void StackMachine::label(std::string name) {
584✔
80
    spillGeneralPurposeRegisters();
584✔
81
    assembly.label(instructionSet->label(name));
584✔
82
}
584✔
83

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

111
void StackMachine::spillGeneralPurposeRegisters() {
858✔
112
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
12,870✔
113
        storeRegisterValue(*reg);
12,012✔
114
    }
858✔
115
}
858✔
116

117
void StackMachine::spillCallerSavedRegisters() {
880✔
118
    for (auto& reg : registers->getCallerSavedRegisters()) {
7,920✔
119
        storeRegisterValue(*reg);
7,040✔
120
    }
880✔
121
}
880✔
122

123
void StackMachine::emitLoad(Value& symbol, Register& dest) {
2,634✔
124
    assembly << instructionSet->mov(memoryOperand(symbol), dest);
2,634✔
125
}
2,634✔
126

127
void StackMachine::emitStore(Register& source, Value& symbol) {
2,248✔
128
    assembly << instructionSet->mov(source, memoryOperand(symbol));
2,248✔
129
}
2,248✔
130

131
// Bind a freshly computed result to its destination symbol. Global object homes are never
132
// register-resident (commit via Address); locals/temps stay register-resident for lazy write-back.
133
void StackMachine::bindResult(Register& reg, Value& result) {
704✔
134
    if (isGlobalHome(result.getName())) {
704✔
135
        emitStore(reg, result);
22✔
136
    } else {
137
        reg.assign(&result);
682✔
138
    }
139
}
704✔
140

141
void StackMachine::assignRegisterToSymbol(Register& reg, Value& symbol) {
1,936✔
142
    if (symbol.isStored()) {
1,936✔
143
        storeRegisterValue(reg);
1,558✔
144
        emitLoad(symbol, reg);
1,558✔
145
    } else if (&reg != &symbol.getAssignedRegister()) {
378✔
146
        storeRegisterValue(reg);
370✔
147
        Register& valueRegister = symbol.getAssignedRegister();
370✔
148
        storeRegisterValue(valueRegister);
370✔
149
        assembly << instructionSet->mov(valueRegister, reg);
370✔
150
    }
151
}
1,936✔
152

153
void StackMachine::compare(std::string leftSymbolName, std::string rightSymbolName) {
178✔
154
    auto& leftSymbol = resolve(leftSymbolName);
178✔
155
    auto& rightSymbol = resolve(rightSymbolName);
178✔
156

157
    if (leftSymbol.isStored() && rightSymbol.isStored()) {
178✔
158
        Register& rightSymbolRegister = assignRegisterTo(rightSymbol);
122✔
159
        assembly << instructionSet->cmp(memoryOperand(leftSymbol), rightSymbolRegister);
122✔
160
    } else if (leftSymbol.isStored()) {
56✔
161
        assembly << instructionSet->cmp(memoryOperand(leftSymbol), rightSymbol.getAssignedRegister());
16✔
162
    } else if (rightSymbol.isStored()) {
40✔
163
        assembly << instructionSet->cmp(leftSymbol.getAssignedRegister(), memoryOperand(rightSymbol));
20✔
164
    } else {
165
        assembly << instructionSet->cmp(leftSymbol.getAssignedRegister(), rightSymbol.getAssignedRegister());
20✔
166
    }
167
}
178✔
168

169
void StackMachine::zeroCompare(std::string symbolName) {
140✔
170
    auto& symbol = resolve(symbolName);
140✔
171
    if (symbol.isStored()) {
140✔
172
        assembly << instructionSet->cmp(memoryOperand(symbol), 0);
130✔
173
    } else {
174
        assembly << instructionSet->cmp(symbol.getAssignedRegister(), 0);
10✔
175
    }
176
}
140✔
177

178
void StackMachine::addressOf(std::string operandName, std::string resultName) {
318✔
179
    auto& operand = resolve(operandName);
318✔
180
    storeInMemory(operand);
318✔
181
    Register& resultRegister = get64BitRegister();
318✔
182
    assembly << instructionSet->lea(memoryOperand(operand), resultRegister);
318✔
183
    bindResult(resultRegister, resolve(resultName));
318✔
184
}
318✔
185

186
void StackMachine::dereference(std::string operandName, std::string lvalueName, std::string resultName) {
78✔
187
    auto& operand = resolve(operandName);
78✔
188
    if (operand.isStored()) {
78✔
189
        assignRegisterTo(operand);
48✔
190
    }
191
    Register& resultRegister = get64BitRegisterExcluding(operand.getAssignedRegister());
78✔
192
    assembly << instructionSet->mov(MemoryOperand::at(operand.getAssignedRegister(), 0), resultRegister);
78✔
193
    bindResult(resultRegister, resolve(resultName));
78✔
194

195
    Register& lvalueRegister = get64BitRegisterExcluding(operand.getAssignedRegister());
78✔
196
    assembly << instructionSet->mov(operand.getAssignedRegister(), lvalueRegister);
78✔
197
    lvalueRegister.assign(&resolve(lvalueName));
78✔
198
}
78✔
199

200
void StackMachine::unaryMinus(std::string operandName, std::string resultName) {
88✔
201
    auto& operand = resolve(operandName);
88✔
202
    if (operand.isStored()) {
88✔
203
        Register& resultRegister = get64BitRegister();
82✔
204
        emitLoad(operand, resultRegister);
82✔
205
        assembly << instructionSet->neg(resultRegister);
82✔
206
        bindResult(resultRegister, resolve(resultName));
82✔
207
    } else {
208
        Register& operandRegister = operand.getAssignedRegister();
6✔
209
        Register& resultRegister = get64BitRegisterExcluding(operand.getAssignedRegister());
6✔
210
        assembly << instructionSet->mov(operandRegister, resultRegister);
6✔
211
        assembly << instructionSet->neg(resultRegister);
6✔
212
        bindResult(resultRegister, resolve(resultName));
6✔
213
    }
214
}
88✔
215

216
void StackMachine::assign(std::string operandName, std::string resultName) {
390✔
217
    auto& operand = resolve(operandName);
390✔
218
    auto& result = resolve(resultName);
390✔
219

220
    if (operand.isStored() && result.isStored()) {
390✔
221
        Register& reg = get64BitRegister();
264✔
222
        emitLoad(operand, reg);
264✔
223
        emitStore(reg, result);
264✔
224
    } else if (operand.isStored()) {
126✔
225
        emitLoad(operand, result.getAssignedRegister());
×
226
    } else if (result.isStored()) {
126✔
227
        emitStore(operand.getAssignedRegister(), result);
126✔
228
    } else {
229
        assembly << instructionSet->mov(operand.getAssignedRegister(), result.getAssignedRegister());
×
230
    }
231
}
390✔
232

233
void StackMachine::assignConstant(std::string constant, std::string resultName) {
2,332✔
234
    auto& result = resolve(resultName);
2,332✔
235
    if (result.isStored()) {
2,332✔
236
        assembly << instructionSet->mov(constant, memoryOperand(result));
2,332✔
237
    } else {
238
        assembly << instructionSet->mov(constant, result.getAssignedRegister());
×
239
    }
240
}
2,332✔
241

242
void StackMachine::lvalueAssign(std::string operandName, std::string resultName) {
56✔
243
    auto& operand = resolve(operandName);
56✔
244
    auto& result = resolve(resultName);
56✔
245

246
    Register& operandRegister = operand.isStored() ? assignRegisterTo(operand) : operand.getAssignedRegister();
56✔
247
    Register& resultRegister = result.isStored() ? assignRegisterExcluding(result, operandRegister) : result.getAssignedRegister();
56✔
248
    assembly << instructionSet->mov(operandRegister, MemoryOperand::at(resultRegister, 0));
56✔
249
}
56✔
250

251
void StackMachine::procedureArgument(std::string argumentName) {
1,924✔
252
    auto argument = &resolve(argumentName);
1,924✔
253
    if (integerArguments.size() < registers->getIntegerArgumentRegisters().size()) {
1,924✔
254
        integerArguments.push_back(argument);
1,862✔
255
    } else {
256
        stackArguments.insert(stackArguments.begin(), argument);
62✔
257
    }
258
}
1,924✔
259

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

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

301
void StackMachine::returnFromProcedure(std::string returnSymbolName) {
456✔
302
    if (!returnSymbolName.empty()) {
456✔
303
        Value& returnSymbol = resolve(returnSymbolName);
418✔
304
        if (returnSymbol.isStored()) {
418✔
305
            emitLoad(returnSymbol, registers->getRetrievalRegister());
404✔
306
        } else if (&registers->getRetrievalRegister() != &returnSymbol.getAssignedRegister()) {
14✔
307
            assembly << instructionSet->mov(returnSymbol.getAssignedRegister(), registers->getRetrievalRegister());
10✔
308
        }
309
    }
310
    popCalleeSavedRegisters();
456✔
311
    assembly << instructionSet->leave();
456✔
312
    assembly << instructionSet->ret();
456✔
313
}
456✔
314

315
void StackMachine::retrieveProcedureReturnValue(std::string returnSymbolName) {
870✔
316
    Value& returnSymbol = resolve(returnSymbolName);
870✔
317
    emitStore(registers->getRetrievalRegister(), returnSymbol);
870✔
318
}
870✔
319

320
void StackMachine::xorCommand(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
321
    Value& leftOperand = resolve(leftOperandName);
10✔
322
    Value& rightOperand = resolve(rightOperandName);
10✔
323
    Register& resultRegister = get64BitRegister();
10✔
324

325
    if (leftOperand.isStored()) {
10✔
326
        emitLoad(leftOperand, resultRegister);
8✔
327
    } else {
328
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
2✔
329
    }
330
    if (rightOperand.isStored()) {
10✔
331
        assembly << instructionSet->xor_(memoryOperand(rightOperand), resultRegister);
10✔
332
    } else {
333
        assembly << instructionSet->xor_(rightOperand.getAssignedRegister(), resultRegister);
×
334
    }
335
    bindResult(resultRegister, resolve(resultName));
10✔
336
}
10✔
337

338
void StackMachine::orCommand(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
339
    Value& leftOperand = resolve(leftOperandName);
10✔
340
    Value& rightOperand = resolve(rightOperandName);
10✔
341
    Register& resultRegister = get64BitRegister();
10✔
342

343
    if (leftOperand.isStored()) {
10✔
344
        emitLoad(leftOperand, resultRegister);
8✔
345
    } else {
346
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
2✔
347
    }
348
    if (rightOperand.isStored()) {
10✔
349
        assembly << instructionSet->or_(memoryOperand(rightOperand), resultRegister);
10✔
350
    } else {
351
        assembly << instructionSet->or_(rightOperand.getAssignedRegister(), resultRegister);
×
352
    }
353
    bindResult(resultRegister, resolve(resultName));
10✔
354
}
10✔
355

356
void StackMachine::andCommand(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
357
    Value& leftOperand = resolve(leftOperandName);
10✔
358
    Value& rightOperand = resolve(rightOperandName);
10✔
359
    Register& resultRegister = get64BitRegister();
10✔
360

361
    if (leftOperand.isStored()) {
10✔
362
        emitLoad(leftOperand, resultRegister);
8✔
363
    } else {
364
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
2✔
365
    }
366
    if (rightOperand.isStored()) {
10✔
367
        assembly << instructionSet->and_(memoryOperand(rightOperand), resultRegister);
10✔
368
    } else {
369
        assembly << instructionSet->and_(rightOperand.getAssignedRegister(), resultRegister);
×
370
    }
371
    bindResult(resultRegister, resolve(resultName));
10✔
372
}
10✔
373

374
void StackMachine::add(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
86✔
375
    Value& leftOperand = resolve(leftOperandName);
86✔
376
    Value& rightOperand = resolve(rightOperandName);
86✔
377
    Register& resultRegister = get64BitRegister();
86✔
378

379
    if (leftOperand.isStored()) {
86✔
380
        emitLoad(leftOperand, resultRegister);
62✔
381
    } else {
382
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
24✔
383
    }
384
    if (rightOperand.isStored()) {
86✔
385
        assembly << instructionSet->add(memoryOperand(rightOperand), resultRegister);
70✔
386
    } else {
387
        assembly << instructionSet->add(rightOperand.getAssignedRegister(), resultRegister);
16✔
388
    }
389
    bindResult(resultRegister, resolve(resultName));
86✔
390
}
86✔
391

392
void StackMachine::sub(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
30✔
393
    Value& leftOperand = resolve(leftOperandName);
30✔
394
    Value& rightOperand = resolve(rightOperandName);
30✔
395
    Register& resultRegister = get64BitRegister();
30✔
396

397
    if (leftOperand.isStored()) {
30✔
398
        emitLoad(leftOperand, resultRegister);
22✔
399
    } else {
400
        assembly << instructionSet->mov(leftOperand.getAssignedRegister(), resultRegister);
8✔
401
    }
402
    if (rightOperand.isStored()) {
30✔
403
        assembly << instructionSet->sub(memoryOperand(rightOperand), resultRegister);
26✔
404
    } else {
405
        assembly << instructionSet->sub(rightOperand.getAssignedRegister(), resultRegister);
4✔
406
    }
407
    bindResult(resultRegister, resolve(resultName));
30✔
408
}
30✔
409

410
void StackMachine::mul(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
16✔
411
    Value& leftOperand = resolve(leftOperandName);
16✔
412
    Value& rightOperand = resolve(rightOperandName);
16✔
413
    Value& result = resolve(resultName);
16✔
414

415
    if (result.getType() != Type::INTEGRAL) {
16✔
416
        throw std::runtime_error{"multiplication of non integers is not implemented"};
×
417
    }
418

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

431
void StackMachine::div(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
432
    Value& leftOperand = resolve(leftOperandName);
10✔
433
    Value& rightOperand = resolve(rightOperandName);
10✔
434
    Value& result = resolve(resultName);
10✔
435

436
    if (result.getType() != Type::INTEGRAL) {
10✔
437
        throw std::runtime_error{"division of non integer types is not implemented"};
×
438
    }
439

440
    Register& multiplicationRegister = registers->getMultiplicationRegister();
10✔
441
    assignRegisterToSymbol(multiplicationRegister, leftOperand);
10✔
442
    storeRegisterValue(registers->getRemainderRegister());
10✔
443
    assembly << instructionSet->xor_(registers->getRemainderRegister(), registers->getRemainderRegister());
10✔
444
    if (rightOperand.isStored()) {
10✔
445
        assembly << instructionSet->idiv(memoryOperand(rightOperand));
10✔
446
    } else {
447
        assembly << instructionSet->idiv(rightOperand.getAssignedRegister());
×
448
    }
449
    bindResult(multiplicationRegister, result);
10✔
450
}
10✔
451

452
void StackMachine::mod(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
10✔
453
    Value& leftOperand = resolve(leftOperandName);
10✔
454
    Value& rightOperand = resolve(rightOperandName);
10✔
455
    Value& result = resolve(resultName);
10✔
456

457
    if (result.getType() != Type::INTEGRAL) {
10✔
458
        throw std::runtime_error{"modular division of non integer types is not implemented"};
×
459
    }
460

461
    Register& multiplicationRegister = registers->getMultiplicationRegister();
10✔
462
    assignRegisterToSymbol(multiplicationRegister, leftOperand);
10✔
463
    storeRegisterValue(registers->getRemainderRegister());
10✔
464
    assembly << instructionSet->xor_(registers->getRemainderRegister(), registers->getRemainderRegister());
10✔
465
    if (rightOperand.isStored()) {
10✔
466
        assembly << instructionSet->idiv(memoryOperand(rightOperand));
10✔
467
    } else {
468
        assembly << instructionSet->idiv(rightOperand.getAssignedRegister());
×
469
    }
470
    bindResult(registers->getRemainderRegister(), result);
10✔
471
}
10✔
472

473
void StackMachine::inc(std::string operandName) {
40✔
474
    Value& operand = resolve(operandName);
40✔
475
    if (operand.isStored()) {
40✔
476
        assembly << instructionSet->inc(memoryOperand(operand));
12✔
477
    } else {
478
        assembly << instructionSet->inc(operand.getAssignedRegister());
28✔
479
    }
480
}
40✔
481

482
void StackMachine::dec(std::string operandName) {
12✔
483
    Value& operand = resolve(operandName);
12✔
484
    if (operand.isStored()) {
12✔
485
        assembly << instructionSet->dec(memoryOperand(operand));
4✔
486
    } else {
487
        assembly << instructionSet->dec(operand.getAssignedRegister());
8✔
488
    }
489
}
12✔
490

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

504
    Value& leftOperand = resolve(leftOperandName);
38✔
505
    Register& resultRegister = get64BitRegisterExcluding(counterRegister);
38✔
506
    assignRegisterToSymbol(resultRegister, leftOperand);
38✔
507
    assembly << (instructionSet.get()->*emitShift)(resultRegister);
38✔
508
    Value& result = resolve(resultName);
38✔
509
    bindResult(resultRegister, result);
38✔
510
}
38✔
511

512
void StackMachine::shl(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
20✔
513
    shiftBy(leftOperandName, rightOperandName, resultName, &InstructionSet::shl);
20✔
514
}
20✔
515

516
void StackMachine::shr(std::string leftOperandName, std::string rightOperandName, std::string resultName) {
18✔
517
    shiftBy(leftOperandName, rightOperandName, resultName, &InstructionSet::shr);
18✔
518
}
18✔
519

520
void StackMachine::storeRegisterValue(Register& reg) {
22,308✔
521
    if (reg.containsUnstoredValue()) {
22,308✔
522
        emitStore(reg, *reg.getValue());
966✔
523
        reg.free();
966✔
524
    }
525
}
22,308✔
526

527
void StackMachine::emptyGeneralPurposeRegisters() {
870✔
528
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
13,050✔
529
        reg->free();
12,180✔
530
    }
870✔
531
}
870✔
532

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

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

537
void StackMachine::pushRegisters(std::vector<Register*> source, std::vector<Register*>& destination) {
440✔
538
    for (auto& reg : source) {
2,640✔
539
        pushRegister(*reg, destination);
2,200✔
540
    }
541
}
440✔
542

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

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

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

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

567
Address StackMachine::addressFromValueFlags(const Value& symbol, int extraOffset) const {
4,436✔
568
    if (symbol.isGlobal()) {
4,436✔
NEW
569
        return Address::globalLabel(symbol.getName(), symbol.getSizeInBytes());
×
570
    }
571
    const FrameBase base = symbol.isFunctionArgument() ? FrameBase::Rbp : FrameBase::Rsp;
4,436✔
572
    return Address::frame(base, memoryOffset(symbol) + extraOffset, symbol.getSizeInBytes());
4,436✔
573
}
574

575
void StackMachine::registerFrameHome(const Value& symbol) {
4,418✔
576
    frameHomes.insert_or_assign(symbol.getName(), addressFromValueFlags(symbol, 0));
4,418✔
577
}
4,418✔
578

NEW
579
bool StackMachine::hasObjectHome(const std::string& name) const {
×
NEW
580
    return frameHomes.count(name) || globalHomes.count(name);
×
581
}
582

583
bool StackMachine::isGlobalHome(const std::string& name) const {
704✔
584
    return globalHomes.count(name) > 0;
704✔
585
}
586

587
Address StackMachine::addressOf(const Value& symbol, int extraOffset) const {
8,060✔
588
    const std::string& name = symbol.getName();
8,060✔
589
    auto frame = frameHomes.find(name);
8,060✔
590
    if (frame != frameHomes.end()) {
8,060✔
591
        const Address& home = frame->second;
7,840✔
592
        if (extraOffset == 0) {
7,840✔
593
            return home;
7,784✔
594
        }
595
        return Address::frame(home.frameBase(), home.offsetBytes() + extraOffset, home.sizeBytes());
56✔
596
    }
597
    auto global = globalHomes.find(name);
220✔
598
    if (global != globalHomes.end()) {
220✔
599
        return global->second;
202✔
600
    }
601
    // Pure temps (and any name without a registered home): derive from Value flags if set.
602
    return addressFromValueFlags(symbol, extraOffset);
18✔
603
}
8,060✔
604

605
MemoryOperand StackMachine::memoryOperand(const Address& address) const {
8,060✔
606
    if (address.isGlobal()) {
8,060✔
607
        return MemoryOperand::global(address.label());
202✔
608
    }
609
    const Register& base = address.frameBase() == FrameBase::Rbp ?
7,858✔
610
            registers->getBasePointer() : registers->getStackPointer();
7,858✔
611
    return MemoryOperand::at(base, address.offsetBytes());
7,858✔
612
}
613

614
MemoryOperand StackMachine::memoryOperand(const Value& symbol, int extraOffset) const {
8,060✔
615
    return memoryOperand(addressOf(symbol, extraOffset));
8,060✔
616
}
617

618
Register& StackMachine::get64BitRegister() {
1,046✔
619
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
1,394✔
620
        if (!reg->containsUnstoredValue()) {
1,394✔
621
            return *reg;
1,046✔
622
        }
623
    }
1,046✔
624
    Register& reg = **registers->getGeneralPurposeRegisters().begin();
×
625
    storeRegisterValue(reg);
×
626
    return reg;
×
627
}
628

629
Register& StackMachine::get64BitRegisterExcluding(Register& registerToExclude) {
206✔
630
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
746✔
631
        if (reg != &registerToExclude && !reg->containsUnstoredValue()) {
742✔
632
            return *reg;
202✔
633
        }
634
    }
206✔
635
    for (auto& reg : registers->getGeneralPurposeRegisters()) {
4✔
636
        if (reg != &registerToExclude) {
4✔
637
            storeRegisterValue(*reg);
4✔
638
            return *reg;
4✔
639
        }
640
    }
4✔
641
    throw std::runtime_error{"unable to get a free register"};
×
642
}
643

644
Register& StackMachine::getCounterRegister() {
38✔
645
    Register& counter = registers->getCounterRegister();
38✔
646
    storeRegisterValue(counter);
38✔
647
    return counter;
38✔
648
}
649

650
Register& StackMachine::assignRegisterTo(Value& symbol) {
174✔
651
    Register& reg = get64BitRegister();
174✔
652
    emitLoad(symbol, reg);
174✔
653
    reg.assign(&symbol);
174✔
654
    return reg;
174✔
655
}
656

657
Register& StackMachine::assignRegisterExcluding(Value& symbol, Register& registerToExclude) {
6✔
658
    Register& reg = get64BitRegisterExcluding(registerToExclude);
6✔
659
    emitLoad(symbol, reg);
6✔
660
    reg.assign(&symbol);
6✔
661
    return reg;
6✔
662
}
663

664
void StackMachine::setScope(std::vector<Value> variables) {
16✔
665
    for (auto& var : variables) {
64✔
666
        scopeValues.insert({var.getName(), var});
48✔
667
        registerFrameHome(var);
48✔
668
    }
669
}
16✔
670

671
Value& StackMachine::resolve(const std::string& name) {
8,806✔
672
    auto local = scopeValues.find(name);
8,806✔
673
    if (local != scopeValues.end()) {
8,806✔
674
        return local->second;
8,604✔
675
    }
676
    return globals.at(name);
202✔
677
}
678

679
} // namespace codegen
680

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