19#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
24struct RelocatorTestHelper {
25 const intptr_t kTrampolineSize =
26 Utils::RoundUp(PcRelativeTrampolineJumpPattern::kLengthInBytes,
27 compiler::target::Instructions::kBarePayloadAlignment);
31#if defined(TARGET_ARCH_ARM64)
32 static constexpr intptr_t kOffsetOfCall = 4;
33#elif defined(TARGET_ARCH_ARM)
34 static constexpr intptr_t kOffsetOfCall = 4;
35#elif defined(TARGET_ARCH_RISCV32)
36 static constexpr intptr_t kOffsetOfCall = 4;
37#elif defined(TARGET_ARCH_RISCV64)
38 static constexpr intptr_t kOffsetOfCall = 4;
40 static constexpr intptr_t kOffsetOfCall = 0;
43 explicit RelocatorTestHelper(Thread* thread)
45 locker(thread, thread->isolate_group()->program_lock()),
46 safepoint_and_growth_scope(thread, SafepointLevel::kGC) {
48 FLAG_precompiled_mode =
true;
50 FLAG_lower_pc_relative_call_distance = -128;
51 FLAG_upper_pc_relative_call_distance = 128;
53 ~RelocatorTestHelper() { FLAG_precompiled_mode =
false; }
55 void CreateInstructions(std::initializer_list<intptr_t> sizes) {
56 for (
auto size : sizes) {
57 codes.Add(&Code::Handle(AllocationInstruction(size)));
61 CodePtr AllocationInstruction(uintptr_t size) {
62 const auto& instructions = Instructions::Handle(Instructions::New(
65 uword addr = instructions.PayloadStart();
66 for (uintptr_t i = 0; i < (size / 4); ++i) {
67 *
reinterpret_cast<uint32_t*
>(addr + 4 * i) =
68 static_cast<uint32_t
>(kBreakInstructionFiller);
71 const auto& code = Code::Handle(Code::New(0));
72 code.SetActiveInstructions(instructions, 0);
73 code.set_instructions(instructions);
77 void EmitPcRelativeCallFunction(intptr_t idx, intptr_t to_idx) {
78 const Code& code = *codes[idx];
79 const Code&
target = *codes[to_idx];
81 EmitCodeFor(code, [&](compiler::Assembler* assembler) {
82#if defined(TARGET_ARCH_ARM64)
83 SPILLS_RETURN_ADDRESS_FROM_LR_TO_REGISTER(
85 compiler::Address(CSP, -2 * kWordSize,
86 compiler::Address::PairPreIndex)));
87#elif defined(TARGET_ARCH_ARM)
88 SPILLS_RETURN_ADDRESS_FROM_LR_TO_REGISTER(
__ PushList((1 <<
LR)));
89#elif defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
92 __ GenerateUnRelocatedPcRelativeCall();
93 AddPcRelativeCallTargetAt(
__ CodeSize(), code,
target);
94#if defined(TARGET_ARCH_ARM64)
95 RESTORES_RETURN_ADDRESS_FROM_REGISTER_TO_LR(
97 compiler::Address(CSP, 2 * kWordSize,
98 compiler::Address::PairPostIndex)));
99#elif defined(TARGET_ARCH_ARM)
100 RESTORES_RETURN_ADDRESS_FROM_REGISTER_TO_LR(
__ PopList((1 <<
LR)));
101#elif defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
108 void EmitReturn42Function(intptr_t idx) {
109 const Code& code = *codes[idx];
110 EmitCodeFor(code, [&](compiler::Assembler* assembler) {
111#if defined(TARGET_ARCH_X64)
112 __ LoadImmediate(RAX, 42);
113#elif defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_ARM64)
114 __ LoadImmediate(R0, 42);
115#elif defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
116 __ LoadImmediate(A0, 42);
122 void EmitCodeFor(
const Code& code,
123 std::function<
void(compiler::Assembler* assembler)> fun) {
124 const auto& inst = Instructions::Handle(code.instructions());
126 compiler::Assembler assembler(
nullptr);
129 const uword addr = inst.PayloadStart();
130 memmove(
reinterpret_cast<void*
>(addr),
131 reinterpret_cast<void*
>(assembler.CodeAddress(0)),
132 assembler.CodeSize());
134 if (FLAG_disassemble) {
135 OS::PrintErr(
"Disassemble:\n");
140 void AddPcRelativeCallTargetAt(intptr_t
offset,
143 const auto& kind_and_offset = Smi::Handle(
144 Smi::New(Code::KindField::encode(Code::kPcRelativeCall) |
145 Code::EntryPointField::encode(Code::kDefaultEntry) |
146 Code::OffsetField::encode(
offset)));
147 AddCall(code,
target, kind_and_offset);
150 void AddCall(
const Code& code,
152 const Smi& kind_and_offset) {
153 auto& call_targets = Array::Handle(code.static_calls_target_table());
154 if (call_targets.IsNull()) {
155 call_targets = Array::New(Code::kSCallTableEntryLength);
157 call_targets = Array::Grow(
158 call_targets, call_targets.Length() + Code::kSCallTableEntryLength);
161 StaticCallsTable
table(call_targets);
163 entry.Set<Code::kSCallTableKindAndOffset>(kind_and_offset);
164 entry.Set<Code::kSCallTableCodeOrTypeTarget>(
target);
165 entry.Set<Code::kSCallTableFunctionTarget>(
166 Function::Handle(Function::null()));
167 code.set_static_calls_target_table(call_targets);
170 void BuildImageAndRunTest(
171 std::function<
void(
const GrowableArray<ImageWriterCommand>&, uword*)>
173 auto&
image = Instructions::Handle();
174 uword entrypoint = 0;
176 GrowableArray<CodePtr> raw_codes;
177 for (
auto code : codes) {
178 raw_codes.Add(code->ptr());
181 GrowableArray<ImageWriterCommand> commands;
182 CodeRelocator::Relocate(thread, &raw_codes, &commands,
185 uword expected_offset = 0;
186 fun(commands, &expected_offset);
188 image = BuildImage(&commands);
189 entrypoint =
image.EntryPoint() + expected_offset;
191 for (intptr_t i = 0; i < commands.length(); ++i) {
192 if (commands[i].op == ImageWriterCommand::InsertBytesOfTrampoline) {
193 delete[] commands[i].insert_trampoline_bytes.buffer;
194 commands[i].insert_trampoline_bytes.buffer =
nullptr;
199#if defined(TARGET_ARCH_X64)
200 EXPECT_EQ(42,
reinterpret_cast<Fun
>(entrypoint)());
201#elif defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_RISCV32)
202 EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(Fun, entrypoint));
203#elif defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV64)
204 EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(Fun, entrypoint));
208 InstructionsPtr BuildImage(GrowableArray<ImageWriterCommand>* commands) {
210 for (intptr_t i = 0; i < commands->length(); ++i) {
211 switch ((*commands)[i].op) {
212 case ImageWriterCommand::InsertBytesOfTrampoline:
213 size += (*commands)[i].insert_trampoline_bytes.buffer_length;
215 case ImageWriterCommand::InsertPadding:
216 size += (*commands)[i].insert_padding.padding_length;
218 case ImageWriterCommand::InsertInstructionOfCode:
219 size += ImageWriter::SizeInSnapshot(Code::InstructionsOf(
220 (*commands)[i].insert_instruction_of_code.code));
225 auto& instructions = Instructions::Handle(Instructions::New(
226 size,
false,
false));
228 uword addr = instructions.PayloadStart();
229 for (intptr_t i = 0; i < commands->length(); ++i) {
230 switch ((*commands)[i].op) {
231 case ImageWriterCommand::InsertBytesOfTrampoline: {
232 const auto entry = (*commands)[i].insert_trampoline_bytes;
233 const auto current_size = entry.buffer_length;
234 ASSERT(addr + current_size <= instructions.PayloadStart() + size);
235 memmove(
reinterpret_cast<void*
>(addr), entry.buffer, current_size);
236 addr += current_size;
239 case ImageWriterCommand::InsertPadding: {
240 const auto entry = (*commands)[i].insert_padding;
241 const auto current_size = entry.padding_length;
242 ASSERT(addr + current_size <= instructions.PayloadStart() + size);
243 memset(
reinterpret_cast<void*
>(addr), 0, current_size);
244 addr += current_size;
247 case ImageWriterCommand::InsertInstructionOfCode: {
248 const auto entry = (*commands)[i].insert_instruction_of_code;
249 const auto current_size =
250 ImageWriter::SizeInSnapshot(Code::InstructionsOf(entry.code));
251 ASSERT(addr + current_size <= instructions.PayloadStart() + size);
252 memmove(
reinterpret_cast<void*
>(addr),
253 reinterpret_cast<void*
>(Instructions::PayloadStart(
254 Code::InstructionsOf(entry.code))),
256 addr += current_size;
262 if (FLAG_write_protect_code) {
263 const uword address = UntaggedObject::ToAddr(instructions.ptr());
264 const auto size = instructions.ptr()->untag()->HeapSize();
265 VirtualMemory::Protect(
reinterpret_cast<void*
>(address), size,
266 VirtualMemory::kReadExecute);
268 CPU::FlushICache(instructions.PayloadStart(), instructions.Size());
270 return instructions.ptr();
274 SafepointWriteRwLocker locker;
275 ForceGrowthSafepointOperationScope safepoint_and_growth_scope;
276 GrowableArray<const Code*> codes;
280 RelocatorTestHelper helper(thread);
281 const intptr_t fmax = FLAG_upper_pc_relative_call_distance;
288 helper.CreateInstructions({
290 fmax - (20 - helper.kOffsetOfCall) - 8,
293 helper.EmitPcRelativeCallFunction(0, 2);
294 helper.EmitReturn42Function(2);
295 helper.BuildImageAndRunTest(
296 [&](
const GrowableArray<ImageWriterCommand>& commands,
297 uword* entry_point) {
298 EXPECT_EQ(3, commands.length());
306 *entry_point = commands[0].expected_offset;
311 RelocatorTestHelper helper(thread);
312 const intptr_t fmax = FLAG_upper_pc_relative_call_distance;
314 helper.CreateInstructions({
316 fmax - (20 - helper.kOffsetOfCall) + 4,
319 helper.EmitPcRelativeCallFunction(0, 2);
320 helper.EmitReturn42Function(2);
321 helper.BuildImageAndRunTest([&](
const GrowableArray<ImageWriterCommand>&
323 uword* entry_point) {
324 EXPECT_EQ(4, commands.length());
336 *entry_point = commands[0].expected_offset;
341 RelocatorTestHelper helper(thread);
342 const intptr_t bmax = -FLAG_lower_pc_relative_call_distance;
344 helper.CreateInstructions({
346 bmax - 8 - helper.kOffsetOfCall,
349 helper.EmitReturn42Function(0);
350 helper.EmitPcRelativeCallFunction(2, 0);
351 helper.BuildImageAndRunTest(
352 [&](
const GrowableArray<ImageWriterCommand>& commands,
353 uword* entry_point) {
354 EXPECT_EQ(3, commands.length());
362 *entry_point = commands[2].expected_offset;
367 RelocatorTestHelper helper(thread);
368 const intptr_t bmax = -FLAG_lower_pc_relative_call_distance;
369 const intptr_t fmax = FLAG_upper_pc_relative_call_distance;
371 helper.CreateInstructions({
373 bmax - 8 - helper.kOffsetOfCall + 4,
375 fmax - (20 - helper.kOffsetOfCall) -
380 helper.EmitReturn42Function(0);
381 helper.EmitPcRelativeCallFunction(2, 0);
382 helper.BuildImageAndRunTest([&](
const GrowableArray<ImageWriterCommand>&
384 uword* entry_point) {
385 EXPECT_EQ(7, commands.length());
402 *entry_point = commands[2].expected_offset;
407 RelocatorTestHelper helper(thread);
408 const intptr_t bmax = -FLAG_lower_pc_relative_call_distance;
410 helper.CreateInstructions({
412 bmax - 8 - helper.kOffsetOfCall + 4,
416 helper.EmitReturn42Function(0);
417 helper.EmitPcRelativeCallFunction(2, 0);
418 helper.BuildImageAndRunTest(
419 [&](
const GrowableArray<ImageWriterCommand>& commands,
420 uword* entry_point) {
421 EXPECT_EQ(5, commands.length());
436 *entry_point = commands[4].expected_offset;
444 PcRelativeCallPattern pattern(
reinterpret_cast<uword>(&instruction));
455 PcRelativeTailCallPattern pattern(
reinterpret_cast<uword>(&instruction));
#define RA(width, name,...)
static constexpr int kLengthInBytes
static constexpr int32_t kLowerCallingRange
static constexpr int kLengthInBytes
static constexpr int32_t kUpperCallingRange
static constexpr int32_t kUpperCallingRange
static constexpr int32_t kLowerCallingRange
#define UNIT_TEST_CASE(name)
#define DECLARE_FLAG(type, name)
@ InsertInstructionOfCode
@ InsertBytesOfTrampoline
#define ISOLATE_UNIT_TEST_CASE(name)