Skip to content

Commit 2ebfc9f

Browse files
rmacnak-googleCommit Bot
authored andcommitted
[vm, ffi] Fix some confused register usage during callbacks.
EmitNativeMove is also used during NativeParamterInstr, not just FfiCallInstr, so the trick with WithIntermediateMarshalling doesn't work. Instead, move arguments to their final register in the prologue of FfiCallInstr and remove conflicting uses within FfiCallInstr. TEST=ffi_2/function_callbacks_test Bug: #48164 Change-Id: I652ab714dbfcb092dc5a23e4596f77136d91ffde Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/240324 Reviewed-by: Daco Harkes <dacoharkes@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
1 parent 54c4829 commit 2ebfc9f

File tree

10 files changed

+86
-85
lines changed

10 files changed

+86
-85
lines changed

runtime/vm/compiler/backend/flow_graph_compiler.cc

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3507,25 +3507,23 @@ void FlowGraphCompiler::EmitNativeMove(
35073507
return;
35083508
}
35093509

3510-
#if !defined(TARGET_ARCH_RISCV32) && !defined(TARGET_ARCH_RISCV64)
35113510
// Split moves from stack to stack, none of the architectures provides
3512-
// memory to memory move instructions. But RISC-V needs to avoid TMP.
3511+
// memory to memory move instructions.
35133512
if (source.IsStack() && destination.IsStack()) {
3514-
Register scratch = TMP;
3515-
if (TMP == kNoRegister) {
3516-
scratch = temp->AllocateTemporary();
3517-
}
3513+
Register scratch = temp->AllocateTemporary();
3514+
ASSERT(scratch != kNoRegister);
3515+
#if defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
3516+
ASSERT(scratch != TMP); // TMP is an argument register.
3517+
ASSERT(scratch != TMP2); // TMP2 is an argument register.
3518+
#endif
35183519
const auto& intermediate =
35193520
*new (zone_) compiler::ffi::NativeRegistersLocation(
35203521
zone_, dst_payload_type, dst_container_type, scratch);
35213522
EmitNativeMove(intermediate, source, temp);
35223523
EmitNativeMove(destination, intermediate, temp);
3523-
if (TMP == kNoRegister) {
3524-
temp->ReleaseTemporary();
3525-
}
3524+
temp->ReleaseTemporary();
35263525
return;
35273526
}
3528-
#endif
35293527

35303528
const bool sign_or_zero_extend = dst_container_size > src_container_size;
35313529

runtime/vm/compiler/backend/flow_graph_compiler_riscv.cc

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -868,15 +868,6 @@ static compiler::OperandSize BytesToOperandSize(intptr_t bytes) {
868868
}
869869
}
870870

871-
// See FfiCallInstr::MakeLocationSummary.
872-
static Register WithIntermediateMarshalling(Register r) {
873-
if (r == A2) return T2; // A2=CODE_REG
874-
if (r == A3) return T3; // A3=TMP
875-
if (r == A4) return T4; // A4=TMP2
876-
if (r == A5) return T5; // A5=PP
877-
return r;
878-
}
879-
880871
void FlowGraphCompiler::EmitNativeMoveArchitecture(
881872
const compiler::ffi::NativeLocation& destination,
882873
const compiler::ffi::NativeLocation& source) {
@@ -894,12 +885,12 @@ void FlowGraphCompiler::EmitNativeMoveArchitecture(
894885
if (source.IsRegisters()) {
895886
const auto& src = source.AsRegisters();
896887
ASSERT(src.num_regs() == 1);
897-
const auto src_reg = WithIntermediateMarshalling(src.reg_at(0));
888+
const auto src_reg = src.reg_at(0);
898889

899890
if (destination.IsRegisters()) {
900891
const auto& dst = destination.AsRegisters();
901892
ASSERT(dst.num_regs() == 1);
902-
const auto dst_reg = WithIntermediateMarshalling(dst.reg_at(0));
893+
const auto dst_reg = dst.reg_at(0);
903894
if (!sign_or_zero_extend) {
904895
#if XLEN == 32
905896
__ MoveRegister(dst_reg, src_reg);
@@ -995,7 +986,7 @@ void FlowGraphCompiler::EmitNativeMoveArchitecture(
995986
if (destination.IsRegisters()) {
996987
const auto& dst = destination.AsRegisters();
997988
ASSERT(dst.num_regs() == 1);
998-
const auto dst_reg = WithIntermediateMarshalling(dst.reg_at(0));
989+
const auto dst_reg = dst.reg_at(0);
999990
ASSERT(!sign_or_zero_extend);
1000991
__ LoadFromOffset(dst_reg, src.base_register(), src.offset_in_bytes(),
1001992
BytesToOperandSize(dst_size));
@@ -1015,14 +1006,8 @@ void FlowGraphCompiler::EmitNativeMoveArchitecture(
10151006
default:
10161007
UNIMPLEMENTED();
10171008
}
1018-
} else if (destination.IsStack()) {
1019-
const auto& dst = destination.AsStack();
1020-
// TMP=A3, here not remapped to T3.
1021-
__ LoadFromOffset(TMP, src.base_register(), src.offset_in_bytes(),
1022-
BytesToOperandSize(src_size));
1023-
__ StoreToOffset(TMP, dst.base_register(), dst.offset_in_bytes(),
1024-
BytesToOperandSize(dst_size));
10251009
} else {
1010+
ASSERT(destination.IsStack());
10261011
UNREACHABLE();
10271012
}
10281013
}

runtime/vm/compiler/backend/il.cc

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6532,7 +6532,8 @@ LocationSummary* FfiCallInstr::MakeLocationSummaryInternal(
65326532

65336533
void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
65346534
const Register saved_fp,
6535-
const Register temp) {
6535+
const Register temp0,
6536+
const Register temp1) {
65366537
if (compiler::Assembler::EmittingComments()) {
65376538
__ Comment("EmitParamMoves");
65386539
}
@@ -6546,7 +6547,7 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
65466547
const auto& pointer_register =
65476548
pointer_location.IsRegisters()
65486549
? pointer_location.AsRegisters().reg_at(0)
6549-
: temp;
6550+
: temp0;
65506551
__ MoveRegister(pointer_register, SPREG);
65516552
__ AddImmediate(pointer_register, marshaller_.PassByPointerStackOffset(
65526553
compiler::ffi::kResultIndex));
@@ -6586,7 +6587,7 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
65866587
? arg_target.AsPointerToMemory().pointer_location()
65876588
: /*arg_target.IsStack()*/ arg_target.Split(zone_, num_defs, i);
65886589

6589-
ConstantTemporaryAllocator temp_alloc(temp);
6590+
ConstantTemporaryAllocator temp_alloc(temp0);
65906591
if (origin.IsConstant()) {
65916592
// Can't occur because we currently don't inline FFI trampolines (see
65926593
// http://dartbug.com/45055), which means all incomming arguments
@@ -6610,11 +6611,11 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
66106611
// TypedData/Pointer data pointed to in temp.
66116612
const auto& dst = compiler::ffi::NativeRegistersLocation(
66126613
zone_, pointer_loc.payload_type(), pointer_loc.container_type(),
6613-
temp);
6614+
temp0);
66146615
compiler->EmitNativeMove(dst, pointer_loc, &temp_alloc);
6615-
__ LoadField(temp,
6616+
__ LoadField(temp0,
66166617
compiler::FieldAddress(
6617-
temp, compiler::target::PointerBase::data_offset()));
6618+
temp0, compiler::target::PointerBase::data_offset()));
66186619

66196620
// Copy chuncks.
66206621
const intptr_t sp_offset =
@@ -6624,16 +6625,16 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
66246625
// space on the stack.
66256626
for (intptr_t i = 0; i < arg_target.payload_type().SizeInBytes();
66266627
i += compiler::target::kWordSize) {
6627-
__ LoadMemoryValue(TMP, temp, i);
6628-
__ StoreMemoryValue(TMP, SPREG, i + sp_offset);
6628+
__ LoadMemoryValue(temp1, temp0, i);
6629+
__ StoreMemoryValue(temp1, SPREG, i + sp_offset);
66296630
}
66306631

66316632
// Store the stack address in the argument location.
6632-
__ MoveRegister(temp, SPREG);
6633-
__ AddImmediate(temp, sp_offset);
6633+
__ MoveRegister(temp0, SPREG);
6634+
__ AddImmediate(temp0, sp_offset);
66346635
const auto& src = compiler::ffi::NativeRegistersLocation(
66356636
zone_, pointer_loc.payload_type(), pointer_loc.container_type(),
6636-
temp);
6637+
temp0);
66376638
compiler->EmitNativeMove(pointer_loc, src, &temp_alloc);
66386639
}
66396640
}

runtime/vm/compiler/backend/il.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5307,18 +5307,18 @@ class FfiCallInstr : public Definition {
53075307
bool is_optimizing,
53085308
const RegList temps) const;
53095309

5310-
// Clobbers both given registers.
5310+
// Clobbers the first two given registers.
53115311
// `saved_fp` is used as the frame base to rebase off of.
5312+
// `temp1` is only used in case of PointerToMemoryLocation.
53125313
void EmitParamMoves(FlowGraphCompiler* compiler,
53135314
const Register saved_fp,
5314-
const Register temp);
5315+
const Register temp0,
5316+
const Register temp1);
53155317
// Clobbers both given temp registers.
53165318
void EmitReturnMoves(FlowGraphCompiler* compiler,
53175319
const Register temp0,
53185320
const Register temp1);
53195321

5320-
void EmitCall(FlowGraphCompiler* compiler, Register target);
5321-
53225322
Zone* const zone_;
53235323
const compiler::ffi::CallMarshaller& marshaller_;
53245324

runtime/vm/compiler/backend/il_arm.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1446,7 +1446,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
14461446
// Reserve space for the arguments that go on the stack (if any), then align.
14471447
__ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes());
14481448

1449-
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1);
1449+
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1, temp2);
14501450

14511451
if (compiler::Assembler::EmittingComments()) {
14521452
__ Comment(is_leaf_ ? "Leaf Call" : "Call");

runtime/vm/compiler/backend/il_arm64.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1270,7 +1270,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
12701270
// Reserve space for the arguments that go on the stack (if any), then align.
12711271
__ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes());
12721272

1273-
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1);
1273+
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1, temp2);
12741274

12751275
if (compiler::Assembler::EmittingComments()) {
12761276
__ Comment(is_leaf_ ? "Leaf Call" : "Call");

runtime/vm/compiler/backend/il_ia32.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,9 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
10441044
// Reserve space for the arguments that go on the stack (if any), then align.
10451045
__ ReserveAlignedFrameSpace(stack_required);
10461046

1047-
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp);
1047+
// No second temp: PointerToMemoryLocation is not used for arguments in ia32.
1048+
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp,
1049+
kNoRegister);
10481050

10491051
if (is_leaf_) {
10501052
// We store the pre-align SP at a fixed offset from the final SP.

runtime/vm/compiler/backend/il_riscv.cc

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,18 +1382,17 @@ LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
13821382
zone, is_optimizing,
13831383
(R(CallingConventions::kSecondNonArgumentRegister) |
13841384
R(CallingConventions::kFfiAnyNonAbiRegister) | R(CALLEE_SAVED_TEMP2)));
1385-
// A3/A4/A5 are blocked during Dart register allocation because they are
1385+
1386+
// A3/A4/A5 are unavailable in normal register allocation because they are
13861387
// assigned to TMP/TMP2/PP. This assignment is important for reducing code
1387-
// size. To work around this for FFI calls, the FFI argument definitions are
1388-
// allocated to other registers and moved to the correct register at the last
1389-
// moment (so there are no conflicting uses of TMP/TMP2/PP).
1390-
// FfiCallInstr itself sometimes also clobbers A2/CODE_REG.
1391-
// See also FfiCallInstr::EmitCall.
1388+
// size. We can't just override the normal blockage of these registers because
1389+
// they may be used by other instructions between the argument's definition
1390+
// and its use in FfiCallInstr.
1391+
// Note that A3/A4/A5 might be not be the 3rd/4th/5th input because of mixed
1392+
// integer and floating-point arguments.
13921393
for (intptr_t i = 0; i < summary->input_count(); i++) {
13931394
if (!summary->in(i).IsRegister()) continue;
1394-
if (summary->in(i).reg() == A2) {
1395-
summary->set_in(i, Location::RegisterLocation(T2));
1396-
} else if (summary->in(i).reg() == A3) {
1395+
if (summary->in(i).reg() == A3) {
13971396
summary->set_in(i, Location::RegisterLocation(T3));
13981397
} else if (summary->in(i).reg() == A4) {
13991398
summary->set_in(i, Location::RegisterLocation(T4));
@@ -1407,6 +1406,20 @@ LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
14071406
#undef R
14081407

14091408
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1409+
// Beware! Do not use CODE_REG/TMP/TMP2/PP within FfiCallInstr as they are
1410+
// assigned to A2/A3/A4/A5, which may be in use as argument registers.
1411+
__ set_constant_pool_allowed(false);
1412+
for (intptr_t i = 0; i < locs()->input_count(); i++) {
1413+
if (!locs()->in(i).IsRegister()) continue;
1414+
if (locs()->in(i).reg() == T3) {
1415+
__ mv(A3, T3);
1416+
} else if (locs()->in(i).reg() == T4) {
1417+
__ mv(A4, T4);
1418+
} else if (locs()->in(i).reg() == T5) {
1419+
__ mv(A5, T5);
1420+
}
1421+
}
1422+
14101423
const Register target = locs()->in(TargetAddressIndex()).reg();
14111424

14121425
// The temps are indexed according to their register number.
@@ -1431,17 +1444,27 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
14311444
__ mv(saved_fp_or_sp, is_leaf_ ? SPREG : FPREG);
14321445

14331446
if (!is_leaf_) {
1434-
// We need to create a dummy "exit frame". It will share the same pool
1435-
// pointer but have a null code object.
1436-
__ LoadObject(CODE_REG, Object::null_object());
1437-
__ set_constant_pool_allowed(false);
1438-
__ EnterDartFrame(0, PP);
1447+
// We need to create a dummy "exit frame".
1448+
// This is EnterDartFrame without accessing A2=CODE_REG or A5=PP.
1449+
if (FLAG_precompiled_mode) {
1450+
__ subi(SP, SP, 2 * compiler::target::kWordSize);
1451+
__ sx(RA, compiler::Address(SP, 1 * compiler::target::kWordSize));
1452+
__ sx(FP, compiler::Address(SP, 0 * compiler::target::kWordSize));
1453+
__ addi(FP, SP, 2 * compiler::target::kWordSize);
1454+
} else {
1455+
__ subi(SP, SP, 4 * compiler::target::kWordSize);
1456+
__ sx(RA, compiler::Address(SP, 3 * compiler::target::kWordSize));
1457+
__ sx(FP, compiler::Address(SP, 2 * compiler::target::kWordSize));
1458+
__ sx(NULL_REG, compiler::Address(SP, 1 * compiler::target::kWordSize));
1459+
__ sx(NULL_REG, compiler::Address(SP, 0 * compiler::target::kWordSize));
1460+
__ addi(FP, SP, 4 * compiler::target::kWordSize);
1461+
}
14391462
}
14401463

14411464
// Reserve space for the arguments that go on the stack (if any), then align.
14421465
__ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes());
14431466

1444-
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1);
1467+
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1, temp2);
14451468

14461469
if (compiler::Assembler::EmittingComments()) {
14471470
__ Comment(is_leaf_ ? "Leaf Call" : "Call");
@@ -1456,10 +1479,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
14561479
__ StoreToOffset(target, THR, compiler::target::Thread::vm_tag_offset());
14571480
#endif
14581481

1459-
EmitCall(compiler, target);
1460-
1461-
ASSERT(!IsCalleeSavedRegister(PP));
1462-
__ RestorePoolPointer();
1482+
__ jalr(target);
14631483

14641484
#if !defined(PRODUCT)
14651485
__ LoadImmediate(temp1, compiler::target::Thread::vm_tag_dart_id());
@@ -1484,7 +1504,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
14841504
__ TransitionGeneratedToNative(target, FPREG, temp1,
14851505
/*enter_safepoint=*/true);
14861506

1487-
EmitCall(compiler, target);
1507+
__ jalr(target);
14881508

14891509
// Update information in the thread object and leave the safepoint.
14901510
__ TransitionNativeToGenerated(temp1, /*leave_safepoint=*/true);
@@ -1500,7 +1520,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
15001520

15011521
// Calls T0 and clobbers R19 (along with volatile registers).
15021522
ASSERT(target == T0);
1503-
EmitCall(compiler, temp1);
1523+
__ jalr(temp1);
15041524
}
15051525

15061526
// Refresh pinned registers values (inc. write barrier mask and null
@@ -1514,27 +1534,18 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
15141534
// Restore the pre-aligned SP.
15151535
__ mv(SPREG, saved_fp_or_sp);
15161536
} else {
1517-
// Although PP is a callee-saved register, it may have been moved by the GC.
1518-
__ LeaveDartFrame(compiler::kRestoreCallerPP);
1537+
__ LeaveDartFrame();
15191538

15201539
// Restore the global object pool after returning from runtime (old space is
15211540
// moving, so the GOP could have been relocated).
15221541
if (FLAG_precompiled_mode) {
15231542
__ SetupGlobalPoolAndDispatchTable();
15241543
}
1525-
1526-
__ set_constant_pool_allowed(true);
15271544
}
1528-
}
15291545

1530-
void FfiCallInstr::EmitCall(FlowGraphCompiler* compiler, Register target) {
1531-
// Marshall certain argument registers at the last possible moment.
1532-
// See FfiCallInstr::MakeLocationSummary for the details.
1533-
if (InputCount() > 2) __ mv(A2, T2); // A2=CODE_REG
1534-
if (InputCount() > 3) __ mv(A3, T3); // A3=TMP
1535-
if (InputCount() > 4) __ mv(A4, T4); // A4=TMP2
1536-
if (InputCount() > 5) __ mv(A5, T5); // A5=PP
1537-
__ jalr(target);
1546+
// PP is a volatile register, so it must be restored even for leaf FFI calls.
1547+
__ RestorePoolPointer();
1548+
__ set_constant_pool_allowed(true);
15381549
}
15391550

15401551
// Keep in sync with NativeEntryInstr::EmitNativeCode.

runtime/vm/compiler/backend/il_x64.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,9 +1226,9 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
12261226
__ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes());
12271227

12281228
if (is_leaf_) {
1229-
EmitParamMoves(compiler, FPREG, saved_fp);
1229+
EmitParamMoves(compiler, FPREG, saved_fp, TMP);
12301230
} else {
1231-
EmitParamMoves(compiler, saved_fp, saved_sp);
1231+
EmitParamMoves(compiler, saved_fp, saved_sp, TMP);
12321232
}
12331233

12341234
if (compiler::Assembler::EmittingComments()) {

runtime/vm/instructions_riscv.cc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,9 @@ bool DecodeLoadObjectFromPoolOrThread(uword pc, const Code& code, Object* obj) {
227227
#endif
228228
if (instr.rs1p() == PP) {
229229
// PP is untagged on RISC-V.
230-
ASSERT(Utils::IsAligned(offset, kWordSize));
230+
if (!Utils::IsAligned(offset, kWordSize)) {
231+
return false; // Being used as argument register A5.
232+
}
231233
intptr_t index = ObjectPool::IndexFromOffset(offset - kHeapObjectTag);
232234
const ObjectPool& pool = ObjectPool::Handle(code.GetObjectPool());
233235
if (!pool.IsNull() && (index < pool.Length()) &&
@@ -249,7 +251,9 @@ bool DecodeLoadObjectFromPoolOrThread(uword pc, const Code& code, Object* obj) {
249251
intptr_t offset = instr.itype_imm();
250252
if (instr.rs1() == PP) {
251253
// PP is untagged on RISC-V.
252-
ASSERT(Utils::IsAligned(offset, kWordSize));
254+
if (!Utils::IsAligned(offset, kWordSize)) {
255+
return false; // Being used as argument register A5.
256+
}
253257
intptr_t index = ObjectPool::IndexFromOffset(offset - kHeapObjectTag);
254258
const ObjectPool& pool = ObjectPool::Handle(code.GetObjectPool());
255259
if (!pool.IsNull() && (index < pool.Length()) &&

0 commit comments

Comments
 (0)