[vm, ffi] Misc fixes for RV32 FFI.

TEST=local
Change-Id: I50f0a848cfd973389a7e903aa89e650aa7420aaf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/247503
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
Ryan Macnak 2022-06-08 19:41:52 +00:00 committed by Commit Bot
parent 743573cb26
commit 6a1749edbc
28 changed files with 501 additions and 50 deletions

View file

@ -892,6 +892,8 @@ class PrecompilerCompilerConfiguration extends CompilerConfiguration
exec = "$simBuildDir/gen_snapshot";
} else if (_isArm64 && _configuration.useQemu) {
exec = "$buildDir/clang_x64/gen_snapshot";
} else if (_isRiscv32 && _configuration.useQemu) {
exec = "$buildDir/x86/gen_snapshot";
} else if (_isRiscv64 && _configuration.useQemu) {
exec = "$buildDir/x64/gen_snapshot";
} else {

View file

@ -1426,6 +1426,13 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
#define R(r) (1 << r)
static Register RemapA3A4A5(Register r) {
if (r == A3) return T3;
if (r == A4) return T4;
if (r == A5) return T5;
return r;
}
static void RemapA3A4A5(LocationSummary* summary) {
// A3/A4/A5 are unavailable in normal register allocation because they are
// assigned to TMP/TMP2/PP. This assignment is important for reducing code
@ -1435,13 +1442,16 @@ static void RemapA3A4A5(LocationSummary* summary) {
// Note that A3/A4/A5 might be not be the 3rd/4th/5th input because of mixed
// integer and floating-point arguments.
for (intptr_t i = 0; i < summary->input_count(); i++) {
if (!summary->in(i).IsRegister()) continue;
if (summary->in(i).reg() == A3) {
summary->set_in(i, Location::RegisterLocation(T3));
} else if (summary->in(i).reg() == A4) {
summary->set_in(i, Location::RegisterLocation(T4));
} else if (summary->in(i).reg() == A5) {
summary->set_in(i, Location::RegisterLocation(T5));
if (summary->in(i).IsRegister()) {
Register r = RemapA3A4A5(summary->in(i).reg());
summary->set_in(i, Location::RegisterLocation(r));
} else if (summary->in(i).IsPairLocation() &&
summary->in(i).AsPairLocation()->At(0).IsRegister()) {
ASSERT(summary->in(i).AsPairLocation()->At(1).IsRegister());
Register r0 = RemapA3A4A5(summary->in(i).AsPairLocation()->At(0).reg());
Register r1 = RemapA3A4A5(summary->in(i).AsPairLocation()->At(1).reg());
summary->set_in(i, Location::Pair(Location::RegisterLocation(r0),
Location::RegisterLocation(r1)));
}
}
}
@ -1461,18 +1471,28 @@ LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
#undef R
static void MoveA3A4A5(FlowGraphCompiler* compiler, Register r) {
if (r == T3) {
__ mv(A3, T3);
} else if (r == T4) {
__ mv(A4, T4);
} else if (r == T5) {
__ mv(A5, T5);
}
}
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// Beware! Do not use CODE_REG/TMP/TMP2/PP within FfiCallInstr as they are
// assigned to A2/A3/A4/A5, which may be in use as argument registers.
__ set_constant_pool_allowed(false);
for (intptr_t i = 0; i < locs()->input_count(); i++) {
if (!locs()->in(i).IsRegister()) continue;
if (locs()->in(i).reg() == T3) {
__ mv(A3, T3);
} else if (locs()->in(i).reg() == T4) {
__ mv(A4, T4);
} else if (locs()->in(i).reg() == T5) {
__ mv(A5, T5);
if (locs()->in(i).IsRegister()) {
MoveA3A4A5(compiler, locs()->in(i).reg());
} else if (locs()->in(i).IsPairLocation() &&
locs()->in(i).AsPairLocation()->At(0).IsRegister()) {
ASSERT(locs()->in(i).AsPairLocation()->At(1).IsRegister());
MoveA3A4A5(compiler, locs()->in(i).AsPairLocation()->At(0).reg());
MoveA3A4A5(compiler, locs()->in(i).AsPairLocation()->At(1).reg());
}
}
@ -7336,34 +7356,75 @@ LocationSummary* BitCastInstr::MakeLocationSummary(Zone* zone, bool opt) const {
void BitCastInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
switch (from()) {
case kUnboxedFloat: {
ASSERT(to() == kUnboxedInt64);
const FpuRegister src = locs()->in(0).fpu_reg();
const Register dst = locs()->out(0).reg();
__ fmvxw(dst, src);
switch (to()) {
case kUnboxedInt32: {
const FpuRegister src = locs()->in(0).fpu_reg();
const Register dst = locs()->out(0).reg();
__ fmvxw(dst, src);
break;
}
case kUnboxedInt64: {
const FpuRegister src = locs()->in(0).fpu_reg();
#if XLEN == 32
const Register dst0 = locs()->out(0).AsPairLocation()->At(0).reg();
const Register dst1 = locs()->out(0).AsPairLocation()->At(1).reg();
__ fmvxw(dst0, src);
__ li(dst1, 0);
#else
const Register dst = locs()->out(0).reg();
__ fmvxw(dst, src);
#endif
break;
}
default:
UNREACHABLE();
}
break;
}
#if XLEN >= 64
case kUnboxedDouble: {
ASSERT(to() == kUnboxedInt64);
const FpuRegister src = locs()->in(0).fpu_reg();
#if XLEN == 32
const Register dst0 = locs()->out(0).AsPairLocation()->At(0).reg();
const Register dst1 = locs()->out(0).AsPairLocation()->At(1).reg();
__ subi(SP, SP, 16);
__ fsd(src, compiler::Address(SP, 0));
__ lw(dst0, compiler::Address(SP, 0));
__ lw(dst1, compiler::Address(SP, 4));
__ addi(SP, SP, 16);
#else
const Register dst = locs()->out(0).reg();
__ fmvxd(dst, src);
#endif
break;
}
#endif
case kUnboxedInt64: {
const Register src = locs()->in(0).reg();
switch (to()) {
#if XLEN >= 64
case kUnboxedDouble: {
const FpuRegister dst = locs()->out(0).fpu_reg();
#if XLEN == 32
const Register src0 = locs()->in(0).AsPairLocation()->At(0).reg();
const Register src1 = locs()->in(0).AsPairLocation()->At(1).reg();
__ subi(SP, SP, 16);
__ sw(src0, compiler::Address(SP, 0));
__ sw(src1, compiler::Address(SP, 4));
__ fld(dst, compiler::Address(SP, 0));
__ addi(SP, SP, 16);
#else
const Register src = locs()->in(0).reg();
__ fmvdx(dst, src);
#endif
break;
}
#endif
case kUnboxedFloat: {
const FpuRegister dst = locs()->out(0).fpu_reg();
#if XLEN == 32
const Register src0 = locs()->in(0).AsPairLocation()->At(0).reg();
__ fmvwx(dst, src0);
#else
const Register src = locs()->in(0).reg();
__ fmvwx(dst, src);
#endif
break;
}
default:

View file

@ -23,7 +23,7 @@ struct AbiAlignmentUint64 {
};
#if defined(HOST_ARCH_X64) || defined(HOST_ARCH_ARM64) || \
defined(HOST_ARCH_RISCV64)
defined(HOST_ARCH_RISCV32) || defined(HOST_ARCH_RISCV64)
static_assert(offsetof(AbiAlignmentDouble, d) == 8,
"FFI transformation alignment");
static_assert(offsetof(AbiAlignmentUint64, i) == 8,

View file

@ -121,8 +121,14 @@ class ArgumentAllocator : public ValueObject {
NativeRegistersLocation(zone_, payload_type, container_type, reg);
}
#elif defined(TARGET_ARCH_RISCV32)
// After using up F registers, start bitcasting to X register pairs.
if (HasAvailableCpuRegisters(2)) {
// After using up F registers, start bitcasting to X register (pairs).
if ((payload_type.SizeInBytes() == 4) && HasAvailableCpuRegisters(1)) {
const Register reg = AllocateCpuRegister();
const auto& container_type = *new (zone_) NativePrimitiveType(kInt32);
return *new (zone_)
NativeRegistersLocation(zone_, payload_type, container_type, reg);
}
if ((payload_type.SizeInBytes() == 8) && HasAvailableCpuRegisters(2)) {
const Register reg1 = AllocateCpuRegister();
const Register reg2 = AllocateCpuRegister();
const auto& container_type = *new (zone_) NativePrimitiveType(kInt64);
@ -149,7 +155,7 @@ class ArgumentAllocator : public ValueObject {
: payload_type_converted;
if (target::kWordSize == 4 && payload_type.SizeInBytes() == 8) {
if (CallingConventions::kArgumentRegisterAlignment ==
kAlignedToWordSizeBut8AlignedTo8) {
kAlignedToWordSizeAndValueSize) {
cpu_regs_used += cpu_regs_used % 2;
}
if (cpu_regs_used + 2 <= CallingConventions::kNumArgRegs) {

View file

@ -121,6 +121,40 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_doublex20) {
RunSignatureTest(Z, "doublex20", arguments, doubleType);
}
UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_mixedx20) {
#if defined(TARGET_ARCH_IS_32_BIT)
const auto& intptrType = *new (Z) NativePrimitiveType(kInt32);
#elif defined(TARGET_ARCH_IS_64_BIT)
const auto& intptrType = *new (Z) NativePrimitiveType(kInt64);
#endif
const auto& floatType = *new (Z) NativePrimitiveType(kFloat);
const auto& doubleType = *new (Z) NativePrimitiveType(kDouble);
auto& arguments = *new (Z) NativeTypes(Z, 20);
arguments.Add(&intptrType);
arguments.Add(&floatType);
arguments.Add(&intptrType);
arguments.Add(&doubleType);
arguments.Add(&intptrType);
arguments.Add(&floatType);
arguments.Add(&intptrType);
arguments.Add(&doubleType);
arguments.Add(&intptrType);
arguments.Add(&floatType);
arguments.Add(&intptrType);
arguments.Add(&doubleType);
arguments.Add(&intptrType);
arguments.Add(&floatType);
arguments.Add(&intptrType);
arguments.Add(&doubleType);
arguments.Add(&intptrType);
arguments.Add(&floatType);
arguments.Add(&intptrType);
arguments.Add(&doubleType);
RunSignatureTest(Z, "mixedx20", arguments, doubleType);
}
// Test with 3-byte struct.
//
// On ia32, result pointer is passed on stack and passed back in eax.

View file

@ -127,13 +127,10 @@ intptr_t NativePrimitiveType::AlignmentInBytesStack() const {
case kAlignedToWordSize:
// The default is to align stack arguments to word size.
return compiler::target::kWordSize;
case kAlignedToWordSizeBut8AlignedTo8: {
// However, arm32 deviates slightly.
if (SizeInBytes() == 8) {
return 8;
}
return compiler::target::kWordSize;
}
case kAlignedToWordSizeAndValueSize:
// However, arm32+riscv32 align to the greater of word size or value size.
return Utils::Maximum(SizeInBytes(),
static_cast<intptr_t>(compiler::target::kWordSize));
case kAlignedToValueSize:
// iOS on arm64 only aligns to size.
return SizeInBytes();

View file

@ -6,17 +6,17 @@ fa4 float
fa5 float
fa6 float
fa7 float
(a0, a1) int64[float]
(a2, a3) int64[float]
(a4, a5) int64[float]
(a6, a7) int64[float]
a0 int32[float]
a1 int32[float]
a2 int32[float]
a3 int32[float]
a4 int32[float]
a5 int32[float]
a6 int32[float]
a7 int32[float]
S+0 float
S+4 float
S+8 float
S+12 float
S+16 float
S+20 float
S+24 float
S+28 float
=>
fa0 float

View file

@ -0,0 +1,22 @@
r0 int64
v0 float
r1 int64
v1 double
r2 int64
v2 float
r3 int64
v3 double
r4 int64
v4 float
r5 int64
v5 double
r6 int64
v6 float
r7 int64
v7 double
S+0 int64
S+8 float
S+16 int64
S+24 double
=>
v0 double

View file

@ -0,0 +1,22 @@
r0 int64
v0 float
r1 int64
v1 double
r2 int64
v2 float
r3 int64
v3 double
r4 int64
v4 float
r5 int64
v5 double
r6 int64
v6 float
r7 int64
v7 double
S+0 int64
S+8 float
S+16 int64
S+24 double
=>
v0 double

View file

@ -0,0 +1,22 @@
r0 int64
v0 float
r1 int64
v1 double
r2 int64
v2 float
r3 int64
v3 double
r4 int64
v4 float
r5 int64
v5 double
r6 int64
v6 float
r7 int64
v7 double
S+0 int64
S+8 float
S+16 int64
S+24 double
=>
v0 double

View file

@ -0,0 +1,22 @@
r0 int64
v0 float
r1 int64
v1 double
r2 int64
v2 float
r3 int64
v3 double
r4 int64
v4 float
r5 int64
v5 double
r6 int64
v6 float
r7 int64
v7 double
S+0 int64
S+8 float
S+16 int64
S+24 double
=>
v0 double

View file

@ -0,0 +1,22 @@
r0 int32
r1 int32[float]
r2 int32
S+0 double
S+8 int32
S+12 float
S+16 int32
S+24 double
S+32 int32
S+36 float
S+40 int32
S+48 double
S+56 int32
S+60 float
S+64 int32
S+72 double
S+80 int32
S+84 float
S+88 int32
S+96 double
=>
(r0, r1) int64[double]

View file

@ -0,0 +1,22 @@
r0 int32
s0 float
r1 int32
d1 double
r2 int32
s1 float
r3 int32
d2 double
S+0 int32
s6 float
S+4 int32
d4 double
S+8 int32
s7 float
S+12 int32
d5 double
S+16 int32
s12 float
S+20 int32
d7 double
=>
q0 double

View file

@ -0,0 +1,22 @@
r0 int32
s0 float
r1 int32
d1 double
r2 int32
s1 float
r3 int32
d2 double
S+0 int32
s6 float
S+4 int32
d4 double
S+8 int32
s7 float
S+12 int32
d5 double
S+16 int32
s12 float
S+20 int32
d7 double
=>
q0 double

View file

@ -0,0 +1,22 @@
S+0 int32
S+4 float
S+8 int32
S+12 double
S+20 int32
S+24 float
S+28 int32
S+32 double
S+40 int32
S+44 float
S+48 int32
S+52 double
S+60 int32
S+64 float
S+68 int32
S+72 double
S+80 int32
S+84 float
S+88 int32
S+92 double
=>
xmm0 double

View file

@ -0,0 +1,22 @@
S+0 int32
S+4 float
S+8 int32
S+12 double
S+20 int32
S+24 float
S+28 int32
S+32 double
S+40 int32
S+44 float
S+48 int32
S+52 double
S+60 int32
S+64 float
S+68 int32
S+72 double
S+80 int32
S+84 float
S+88 int32
S+92 double
=>
xmm0 double

View file

@ -0,0 +1,22 @@
S+0 int32
S+4 float
S+8 int32
S+12 double
S+20 int32
S+24 float
S+28 int32
S+32 double
S+40 int32
S+44 float
S+48 int32
S+52 double
S+60 int32
S+64 float
S+68 int32
S+72 double
S+80 int32
S+84 float
S+88 int32
S+92 double
=>
xmm0 double

View file

@ -0,0 +1,22 @@
a0 int32
fa0 float
a1 int32
fa1 double
a2 int32
fa2 float
a3 int32
fa3 double
a4 int32
fa4 float
a5 int32
fa5 double
a6 int32
fa6 float
a7 int32
fa7 double
S+0 int32
S+4 float
S+8 int32
S+16 double
=>
fa0 double

View file

@ -0,0 +1,22 @@
a0 int64
fa0 float
a1 int64
fa1 double
a2 int64
fa2 float
a3 int64
fa3 double
a4 int64
fa4 float
a5 int64
fa5 double
a6 int64
fa6 float
a7 int64
fa7 double
S+0 int64
S+8 float
S+16 int64
S+24 double
=>
fa0 double

View file

@ -0,0 +1,22 @@
rdi int64
xmm0 float
rsi int64
xmm1 double
rdx int64
xmm2 float
rcx int64
xmm3 double
r8 int64
xmm4 float
r9 int64
xmm5 double
S+0 int64
xmm6 float
S+8 int64
xmm7 double
S+16 int64
S+24 float
S+32 int64
S+40 double
=>
xmm0 double

View file

@ -0,0 +1,22 @@
rdi int64
xmm0 float
rsi int64
xmm1 double
rdx int64
xmm2 float
rcx int64
xmm3 double
r8 int64
xmm4 float
r9 int64
xmm5 double
S+0 int64
xmm6 float
S+8 int64
xmm7 double
S+16 int64
S+24 float
S+32 int64
S+40 double
=>
xmm0 double

View file

@ -0,0 +1,22 @@
rdi int64
xmm0 float
rsi int64
xmm1 double
rdx int64
xmm2 float
rcx int64
xmm3 double
r8 int64
xmm4 float
r9 int64
xmm5 double
S+0 int64
xmm6 float
S+8 int64
xmm7 double
S+16 int64
S+24 float
S+32 int64
S+40 double
=>
xmm0 double

View file

@ -0,0 +1,22 @@
rcx int64
xmm1 float
r8 int64
xmm3 double
S+0 int64
S+8 float
S+16 int64
S+24 double
S+32 int64
S+40 float
S+48 int64
S+56 double
S+64 int64
S+72 float
S+80 int64
S+88 double
S+96 int64
S+104 float
S+112 int64
S+120 double
=>
xmm0 double

View file

@ -1,4 +1,4 @@
Struct(size: 72, field alignment: 8, stack alignment: 4, members: {
Struct(size: 72, field alignment: 8, stack alignment: 8, members: {
0: int8,
2: int16,
4: int32,

View file

@ -135,7 +135,7 @@ class StubCodeCompiler : public AllStatic {
static constexpr intptr_t kNativeCallbackTrampolineStackDelta = 2;
#elif defined(TARGET_ARCH_RISCV32)
static constexpr intptr_t kNativeCallbackTrampolineSize = 8;
static constexpr intptr_t kNativeCallbackSharedStubSize = 198;
static constexpr intptr_t kNativeCallbackSharedStubSize = 230;
static constexpr intptr_t kNativeCallbackTrampolineStackDelta = 2;
#elif defined(TARGET_ARCH_RISCV64)
static constexpr intptr_t kNativeCallbackTrampolineSize = 8;

View file

@ -658,11 +658,11 @@ class CallingConventions {
// Whether larger than wordsize arguments are aligned to even registers.
static constexpr AlignmentStrategy kArgumentRegisterAlignment =
kAlignedToWordSizeBut8AlignedTo8;
kAlignedToWordSizeAndValueSize;
// How stack arguments are aligned.
static constexpr AlignmentStrategy kArgumentStackAlignment =
kAlignedToWordSizeBut8AlignedTo8;
kAlignedToWordSizeAndValueSize;
// How fields in compounds are aligned.
#if defined(DART_TARGET_OS_MACOS_IOS)

View file

@ -16,9 +16,8 @@ enum AlignmentStrategy {
kAlignedToValueSizeBut8AlignedTo4,
// Align to the architecture size.
kAlignedToWordSize,
// Align to the architecture size, but align 8 byte-sized values to 8 bytes.
// Both double and int64.
kAlignedToWordSizeBut8AlignedTo8,
// Align to the greater of architecture size or value size.
kAlignedToWordSizeAndValueSize,
};
// Minimum size strategies for how to store values.

View file

@ -511,7 +511,7 @@ class CallingConventions {
// How stack arguments are aligned.
static constexpr AlignmentStrategy kArgumentStackAlignment =
kAlignedToWordSize;
kAlignedToWordSizeAndValueSize;
// How fields in compounds are aligned.
static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize;