1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
|
// SPDX-License-Identifier: GPL-3.0-or-later
#include "rcc/CodeGen/LowLevelType.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineCFGUpdater.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineInstr.h"
#include "rcc/CodeGen/MachineMemOperand.h"
#include "rcc/CodeGen/MachineOperand.h"
#include "rcc/CodeGen/MachineRegisterInfo.h"
#include "rcc/CodeGen/RegisterMask.h"
#include <array>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace {
class TestContext final {
public:
void expect(bool Condition, std::string_view Message) {
if (Condition)
return;
std::cerr << Message << '\n';
++FailureCount;
}
[[nodiscard]] int result() const noexcept {
return FailureCount == 0 ? 0 : 1;
}
private:
int FailureCount = 0;
};
[[nodiscard]] std::unique_ptr<rcc::MachineFunction>
createFunction(TestContext &Context, std::string Name, std::uint64_t ID) {
auto Function =
rcc::MachineFunction::create(std::move(Name), rcc::MachineFunctionStableID::fromValue(ID));
Context.expect(Function.has_value(), "valid machine function construction must succeed");
if (!Function)
return nullptr;
return std::move(*Function);
}
[[nodiscard]] rcc::MachineBasicBlock *createLinkedBlock(TestContext &Context,
rcc::MachineFunction &Function) {
auto Block = Function.createBasicBlock();
Context.expect(Block.has_value(), "basic block allocation must succeed");
if (!Block)
return nullptr;
auto LinkResult = Function.appendBlock(**Block);
Context.expect(LinkResult.has_value(), "basic block insertion must succeed");
if (!LinkResult)
return nullptr;
return *Block;
}
[[nodiscard]] bool addRegisterOperand(TestContext &Context, rcc::MachineInstr &Instruction,
rcc::Register Reg, rcc::RegisterOperandFlags Flags) {
auto Operand = rcc::MachineOperand::registerOperand(Reg, Flags);
Context.expect(Operand.has_value(), "valid register operand construction must succeed");
if (!Operand)
return false;
auto AddResult = Instruction.addOperand(std::move(*Operand));
Context.expect(AddResult.has_value(), "register operand insertion must succeed");
return AddResult.has_value();
}
[[nodiscard]] std::size_t countOperands(rcc::RegOperandRange Range) {
std::size_t Count = 0;
for ([[maybe_unused]] const rcc::MachineOperand &Operand : Range)
++Count;
return Count;
}
rcc::MachineMemOperandDesc makeLoadDescription() {
rcc::MachineMemOperandDesc Description;
Description.Accesses = rcc::MemoryAccessFlags{
rcc::MemoryAccessFlag::Load,
rcc::MemoryAccessFlag::Dereferenceable,
};
Description.Size = *rcc::MemorySize::fixed(8);
Description.Align = *rcc::Alignment::create(8);
Description.Base = rcc::MemoryBase::frameIndex(rcc::FrameIndex::fromValue(0));
return Description;
}
void testOperandKinds(TestContext &Context) {
auto Function = createFunction(Context, "operands", 1);
if (!Function)
return;
auto Block = Function->createBasicBlock();
Context.expect(Block.has_value(), "operand test block allocation must succeed");
if (!Block)
return;
const std::array<std::uint64_t, 1> HalfWords{0x3c00};
auto HalfBits = rcc::FloatingPointBits::create(rcc::FloatSemantics::IEEEHalf, HalfWords);
Context.expect(HalfBits.has_value(), "well-sized floating immediate must be accepted");
if (!HalfBits)
return;
Context.expect(HalfBits->bitWidth() == 16 && HalfBits->words()[0] == 0x3c00,
"floating immediate must preserve semantics and raw bits");
const std::array<std::uint64_t, 1> OverflowHalfWords{0x10000};
Context.expect(!rcc::FloatingPointBits::create(rcc::FloatSemantics::IEEEHalf, OverflowHalfWords),
"floating immediate bits above semantics width must be rejected");
const std::array<std::uint64_t, 2> TooManyHalfWords{0, 0};
Context.expect(!rcc::FloatingPointBits::create(rcc::FloatSemantics::IEEEHalf, TooManyHalfWords),
"floating immediate word-count mismatch must be rejected");
auto Virtual = Function->registerInfo().createVirtualRegister(*rcc::LLT::makeInteger(32));
Context.expect(Virtual.has_value(), "operand test virtual register must be created");
if (!Virtual)
return;
auto RegOperand = rcc::MachineOperand::registerOperand(
*Virtual, rcc::RegisterOperandFlags{rcc::RegisterOperandFlag::Use});
Context.expect(RegOperand && RegOperand->kind() == rcc::MachineOperand::Kind::Register,
"register operand must preserve its kind");
Context.expect(!rcc::MachineOperand::registerOperand(*Virtual, {}),
"register operand without def/use role must be rejected");
Context.expect(!rcc::MachineOperand::registerOperand(
*Virtual, rcc::RegisterOperandFlags{rcc::RegisterOperandFlag::Define,
rcc::RegisterOperandFlag::Kill}),
"kill flag on register definition must be rejected");
auto Immediate = rcc::MachineOperand::immediate(-9, 17);
Context.expect(Immediate.kind() == rcc::MachineOperand::Kind::Immediate &&
*Immediate.getIf<std::int64_t>() == -9 && Immediate.targetFlags() == 17,
"immediate operand must preserve value and target flags");
auto Floating = rcc::MachineOperand::floatingImmediate(*HalfBits);
Context.expect(Floating.kind() == rcc::MachineOperand::Kind::FloatingImmediate,
"floating operand must preserve its kind");
auto BlockRef = rcc::MachineOperand::basicBlock(**Block);
Context.expect(BlockRef.getIf<rcc::BasicBlockOperand>()->Block == *Block,
"basic block operand must preserve its target");
auto Frame = rcc::MachineOperand::frameIndex(rcc::FrameIndex::fromValue(4), -8);
Context.expect(Frame.getIf<rcc::FrameIndexOperand>()->Offset == -8,
"frame index operand must preserve its offset");
auto Constant = rcc::MachineOperand::constantPool(rcc::ConstantPoolIndex::fromValue(2), 3);
Context.expect(Constant.kind() == rcc::MachineOperand::Kind::ConstantPoolIndex,
"constant pool operand must preserve its kind");
auto Jump = rcc::MachineOperand::jumpTable(rcc::JumpTableIndex::fromValue(5));
Context.expect(Jump.kind() == rcc::MachineOperand::Kind::JumpTableIndex,
"jump table operand must preserve its kind");
auto Global = rcc::MachineOperand::globalSymbol("global", 12);
auto External = rcc::MachineOperand::externalSymbol("external", -4);
auto BlockAddress = rcc::MachineOperand::blockAddress("function", "block", 2);
Context.expect(Global && External && BlockAddress, "valid symbol operands must be accepted");
Context.expect(!rcc::MachineOperand::globalSymbol(""),
"empty global symbol operand must be rejected");
Context.expect(!rcc::MachineOperand::blockAddress("", "block"),
"incomplete block address operand must be rejected");
auto Mask = std::make_shared<const rcc::RegisterMask>(std::vector<std::uint64_t>{0xffff});
auto MaskOperand = rcc::MachineOperand::registerMask(Mask);
Context.expect(MaskOperand && MaskOperand->kind() == rcc::MachineOperand::Kind::RegisterMask,
"register mask operand must preserve shared immutable mask");
Context.expect(!rcc::MachineOperand::registerMask(nullptr),
"null register mask operand must be rejected");
auto Metadata = rcc::MachineOperand::metadata("source-marker");
Context.expect(Metadata && Metadata->kind() == rcc::MachineOperand::Kind::Metadata,
"metadata operand must preserve its kind");
Context.expect(!rcc::MachineOperand::metadata(""), "empty metadata marker must be rejected");
auto Target = rcc::MachineOperand::targetIndex(rcc::TargetIndex::fromValue(8), 9);
Context.expect(Target.getIf<rcc::TargetIndexOperand>()->Offset == 9,
"target index operand must preserve its offset");
auto MCSymbol = rcc::MachineOperand::mcSymbol(".Ltmp0", 1);
Context.expect(MCSymbol && MCSymbol->kind() == rcc::MachineOperand::Kind::MCSymbol,
"MC symbol precursor must preserve its kind");
const auto Clone = Immediate.cloneDetached();
Context.expect(Clone.parent() == nullptr &&
*Clone.getIf<std::int64_t>() == *Immediate.getIf<std::int64_t>(),
"detached operand clone must preserve payload without ownership");
Context.expect(!Immediate.setRegister(*Virtual),
"register mutation on non-register operand must be rejected");
}
void testRegisterInfoAndOperandMovement(TestContext &Context) {
auto Function = createFunction(Context, "def_use", 2);
if (!Function)
return;
rcc::MachineBasicBlock *Block = createLinkedBlock(Context, *Function);
if (Block == nullptr)
return;
auto I32 = rcc::LLT::makeInteger(32);
Context.expect(I32.has_value(), "i32 construction for MRI test must succeed");
if (!I32)
return;
auto V0 = Function->registerInfo().createVirtualRegister(*I32);
auto V1 = Function->registerInfo().createVirtualRegister(*I32);
Context.expect(V0 && V1, "MRI must allocate monotonic virtual registers");
if (!V0 || !V1)
return;
Context.expect(V0->virtualIndex() == 0 && V1->virtualIndex() == 1,
"MRI virtual register indices must start at zero and increase");
auto PhysReg = rcc::PhysicalRegister::create(7);
Context.expect(PhysReg.has_value(), "MRI test physical register must be valid");
if (!PhysReg)
return;
const rcc::Register Physical = rcc::Register::fromPhysical(*PhysReg);
auto Instruction = Function->createInstruction(rcc::Opcode::fromValue(1));
Context.expect(Instruction.has_value(), "machine instruction allocation must succeed");
if (!Instruction)
return;
rcc::MachineInstr *Instr = *Instruction;
if (!addRegisterOperand(Context, *Instr, *V0,
rcc::RegisterOperandFlags{rcc::RegisterOperandFlag::Define}) ||
!addRegisterOperand(Context, *Instr, *V0,
rcc::RegisterOperandFlags{rcc::RegisterOperandFlag::Use}) ||
!addRegisterOperand(Context, *Instr, *V0,
rcc::RegisterOperandFlags{rcc::RegisterOperandFlag::Use,
rcc::RegisterOperandFlag::Debug}) ||
!addRegisterOperand(Context, *Instr, Physical,
rcc::RegisterOperandFlags{rcc::RegisterOperandFlag::Use})) {
return;
}
Context.expect(Function->registerInfo().useEmpty(*V0),
"detached instruction operands must not enter def-use chains");
auto LinkInstruction = Block->appendInstruction(*Instr);
Context.expect(LinkInstruction.has_value(), "instruction insertion must succeed");
if (!LinkInstruction)
return;
Context.expect(countOperands(Function->registerInfo().regOperands(*V0)) == 3,
"def-use chain must include definition, use, and debug use");
Context.expect(countOperands(Function->registerInfo().useOperands(*V0)) == 2,
"use iterator must include ordinary and debug uses");
Context.expect(countOperands(Function->registerInfo().useOperands(*V0, false)) == 1,
"nodbg use iterator must filter debug operands");
Context.expect(Function->registerInfo().getUniqueVRegDef(*V0) == Instr->operandAt(0),
"unique virtual register definition must be discoverable");
Context.expect(countOperands(Function->registerInfo().regOperands(Physical)) == 1,
"physical registers must participate in exact def-use chains");
rcc::MachineOperand *const OldOperandAddress = Instr->operandAt(0);
Instr->reserveOperands(128);
Context.expect(Instr->operandAt(0) != nullptr && Instr->operandAt(0)->parent() == Instr,
"operand relocation must preserve instruction ownership");
Context.expect(Function->registerInfo().verifyDefUseChains().has_value(),
"operand relocation must repair intrusive def-use links");
Context.expect(OldOperandAddress != Instr->operandAt(0) ||
Function->registerInfo().getUniqueVRegDef(*V0) == Instr->operandAt(0),
"def-use head must follow an operand when its address changes");
auto TieResult = Instr->tieOperands(0, 1);
Context.expect(TieResult.has_value(), "valid def-use operand tie must succeed");
const auto DetachedTieClone = Instr->operandAt(0)->cloneDetached();
Context.expect(!DetachedTieClone.tiedOperandIndex(),
"single detached operand clone must not retain an instruction-local tie");
auto InsertResult = Instr->insertOperand(0, rcc::MachineOperand::immediate(5));
Context.expect(InsertResult.has_value(), "operand insertion before a tie must succeed");
Context.expect(Instr->operandAt(1)->tiedOperandIndex() == 2 &&
Instr->operandAt(2)->tiedOperandIndex() == 1,
"operand insertion must rewrite both tie indices");
auto EraseResult = Instr->eraseOperand(0);
Context.expect(EraseResult.has_value(), "temporary operand erasure must succeed");
Context.expect(Instr->operandAt(0)->tiedOperandIndex() == 1 &&
Instr->operandAt(1)->tiedOperandIndex() == 0,
"operand erasure must rewrite both tie indices");
Context.expect(Instr->untieOperand(0).has_value(), "reciprocal operand tie must be removable");
auto SetRegisterResult = Instr->operandAt(1)->setRegister(*V1);
Context.expect(SetRegisterResult.has_value(), "register operand rebinding must succeed");
Context.expect(Function->registerInfo().useEmpty(*V0, false),
"register rebinding must unlink the old ordinary use");
Context.expect(Function->registerInfo().hasOneUse(*V1),
"register rebinding must link the new use");
auto ReplaceResult = Function->registerInfo().replaceRegWith(*V1, *V0);
Context.expect(ReplaceResult && *ReplaceResult == 1,
"replaceRegWith must rewrite every linked operand exactly once");
Context.expect(Function->registerInfo().verifyDefUseChains().has_value(),
"register replacement must preserve exact def-use chains");
for (std::int64_t Value = 0; Value < 64; ++Value) {
auto Result = Instr->addOperand(rcc::MachineOperand::immediate(Value));
Context.expect(Result.has_value(), "variadic immediate operand insertion must succeed");
}
Context.expect(Function->registerInfo().verifyDefUseChains().has_value(),
"repeated operand growth must preserve def-use links");
auto &MRI = Function->registerInfo();
Context.expect(MRI.setRegisterBank(*V0, rcc::RegisterBankID::fromValue(3)).has_value(),
"MRI must record a register bank");
Context.expect(MRI.setRegisterClass(*V0, rcc::RegisterClassID::fromValue(4)).has_value(),
"MRI must record a register class");
Context.expect(MRI.addAllocationHint(*V0, *PhysReg).has_value(),
"MRI must record a physical allocation hint");
auto V0Info = MRI.virtualRegisterInfo(*V0);
Context.expect(V0Info && V0Info->get().Type == I32 &&
V0Info->get().Bank == rcc::RegisterBankID::fromValue(3) &&
V0Info->get().RegClass == rcc::RegisterClassID::fromValue(4),
"MRI virtual register properties must evolve without losing LLT");
Context.expect(MRI.addLiveIn(*PhysReg, *V0).has_value(),
"MRI must record function physical live-ins");
Context.expect(!MRI.addLiveIn(*PhysReg, *V0),
"duplicate function physical live-ins must be rejected");
Context.expect(MRI.reservePhysicalRegister(*PhysReg).has_value(),
"MRI must record reserved physical registers");
Context.expect(MRI.isReserved(*PhysReg),
"MRI reserved-register query must find recorded register");
auto DeadVReg = MRI.createVirtualRegister(*I32);
Context.expect(DeadVReg && MRI.eraseVirtualRegister(*DeadVReg).has_value(),
"unused virtual register must become a tombstone");
auto LaterVReg = MRI.createVirtualRegister(*I32);
Context.expect(LaterVReg && LaterVReg->virtualIndex() != DeadVReg->virtualIndex(),
"MRI must not reuse tombstoned virtual register indices");
Context.expect(!MRI.eraseVirtualRegister(*V0),
"virtual register with linked operands must not be erased");
auto ZombieVReg = MRI.createVirtualRegister(*I32);
auto ZombieInstr = Function->createInstruction(rcc::Opcode::fromValue(2));
Context.expect(ZombieVReg && ZombieInstr,
"detached tombstone validation setup must allocate objects");
if (ZombieVReg && ZombieInstr) {
Context.expect(addRegisterOperand(Context, **ZombieInstr, *ZombieVReg,
rcc::RegisterOperandFlags{rcc::RegisterOperandFlag::Use}),
"detached instruction must initially accept its live virtual register");
Context.expect(MRI.eraseVirtualRegister(*ZombieVReg).has_value(),
"detached operand must not make an unpublished vreg live");
const std::size_t OriginalInstructionCount = Block->instructionCount();
Context.expect(!Block->appendInstruction(**ZombieInstr),
"instruction insertion must reject a tombstoned virtual register");
Context.expect((*ZombieInstr)->parent() == nullptr &&
Block->instructionCount() == OriginalInstructionCount,
"failed instruction insertion must not mutate block layout");
Context.expect(!(*ZombieInstr)->operandAt(0)->setRegister(*ZombieVReg),
"detached register mutation must reject a tombstoned vreg");
}
auto UnlinkResult = Block->removeInstruction(*Instr);
Context.expect(UnlinkResult.has_value(), "instruction removal must succeed");
Context.expect(MRI.useEmpty(*V0) && MRI.getUniqueVRegDef(*V0) == nullptr,
"instruction removal must unlink every register operand");
Context.expect(MRI.verifyDefUseChains().has_value(),
"empty def-use lists after removal must remain structurally valid");
Context.expect(Block->appendInstruction(*Instr).has_value(),
"detached instruction must be reinsertable");
Context.expect(MRI.getUniqueVRegDef(*V0) == Instr->operandAt(0),
"instruction reinsertion must rebuild register links");
}
void testOwnershipCFGAndMemory(TestContext &Context) {
auto Function = createFunction(Context, "cfg", 3);
auto OtherFunction = createFunction(Context, "other", 4);
if (!Function || !OtherFunction)
return;
rcc::MachineBasicBlock *Entry = createLinkedBlock(Context, *Function);
rcc::MachineBasicBlock *Left = createLinkedBlock(Context, *Function);
rcc::MachineBasicBlock *Right = createLinkedBlock(Context, *Function);
rcc::MachineBasicBlock *Foreign = createLinkedBlock(Context, *OtherFunction);
if (Entry == nullptr || Left == nullptr || Right == nullptr || Foreign == nullptr)
return;
Context.expect(Function->blockCount() == 3,
"machine function block list must preserve layout count");
Context.expect(Entry->layoutIndex() == 0 && Left->layoutIndex() == 1 && Right->layoutIndex() == 2,
"block layout indices must follow intrusive layout order");
auto Half = rcc::BranchProbability::fromFraction(1, 2);
Context.expect(Half.has_value(), "CFG test probability must be valid");
if (!Half)
return;
auto CFG = Function->cfgUpdater();
Context.expect(CFG.addSuccessor(*Entry, *Left, *Half).has_value() &&
CFG.addSuccessor(*Entry, *Left, *Half).has_value(),
"duplicate semantic branches must merge into one CFG successor");
Context.expect(Entry->successors().size() == 1 && Left->predecessors().size() == 1,
"CFG must not contain duplicate successor or predecessor entries");
Context.expect(CFG.addSuccessor(*Entry, *Right, *Half).has_value(),
"second CFG successor must be insertable");
Context.expect(!CFG.addSuccessor(*Entry, *Foreign, *Half),
"cross-function CFG edge must be rejected");
Context.expect(!CFG.replaceSuccessor(*Entry, *Left, *Foreign) &&
Entry->successors().size() == 2 && Left->predecessors().size() == 1,
"cross-function CFG replacement must fail without mutating existing edges");
Context.expect(CFG.replaceSuccessor(*Entry, *Left, *Right).has_value(),
"CFG successor replacement must succeed");
Context.expect(Entry->successors().size() == 1 && Left->predecessors().empty() &&
Right->predecessors().size() == 1,
"CFG replacement must merge duplicate target and repair predecessors");
Context.expect(CFG.transferSuccessors(*Entry, *Left).has_value(),
"CFG successor transfer must succeed");
Context.expect(Entry->successors().empty() && Left->successors().size() == 1 &&
Right->predecessors()[0] == Left,
"CFG successor transfer must update both directions");
Context.expect(CFG.disconnectBlock(*Left).has_value(),
"CFG block disconnection must remove incoming and outgoing edges");
Context.expect(Left->successors().empty() && Right->predecessors().empty(),
"CFG disconnection must leave no stale reciprocal edges");
auto Instr = Function->createInstruction(rcc::Opcode::fromValue(9));
auto NextInstr = Function->createInstruction(rcc::Opcode::fromValue(10));
Context.expect(Instr && NextInstr, "instruction arena allocation must succeed");
if (!Instr || !NextInstr)
return;
Context.expect(Entry->appendInstruction(**Instr).has_value(),
"first block instruction must be inserted");
Context.expect(Entry->insertInstructionBefore(**Instr, **NextInstr).has_value(),
"instruction insertion before a stable node must succeed");
Context.expect(Entry->firstInstruction() == *NextInstr && Entry->lastInstruction() == *Instr &&
Entry->instructionCount() == 2,
"intrusive instruction list must preserve order and endpoints");
auto CrossBlockOperand = rcc::MachineOperand::basicBlock(*Foreign);
Context.expect(!(*Instr)->addOperand(std::move(CrossBlockOperand)),
"instruction must reject a block operand from another function");
auto MemOperand = Function->createMemOperand(makeLoadDescription());
auto ForeignMemOperand = OtherFunction->createMemOperand(makeLoadDescription());
Context.expect(MemOperand && ForeignMemOperand,
"function arena must allocate immutable memory operands");
if (MemOperand && ForeignMemOperand) {
Context.expect((*Instr)->addMemOperand(**MemOperand).has_value(),
"instruction must accept a memory operand owned by its function");
Context.expect(!(*Instr)->addMemOperand(**ForeignMemOperand),
"instruction must reject a memory operand from another function");
}
const rcc::MachineBasicBlock *StableEntryAddress = Entry;
const rcc::MachineInstr *StableInstrAddress = *Instr;
for (std::uint32_t Index = 0; Index < 128; ++Index) {
auto NewBlock = Function->createBasicBlock();
auto NewInstr = Function->createInstruction(rcc::Opcode::fromValue(Index + 100));
Context.expect(NewBlock && NewInstr, "arena growth must continue to allocate objects");
}
Context.expect(StableEntryAddress == Entry && StableInstrAddress == *Instr,
"arena growth must preserve block and instruction addresses");
Context.expect(Function->removeBlock(*Right).has_value(),
"disconnected empty block must be removable from layout");
Context.expect(!Right->isLinked() && Function->blockCount() == 2,
"removed block must leave layout while retaining stable storage");
}
} // namespace
int main() {
TestContext Context;
testOperandKinds(Context);
testRegisterInfoAndOperandMovement(Context);
testOwnershipCFGAndMemory(Context);
return Context.result();
}
|