[vm/ffi] Correctly sign- or zero-extend arguments and return values from native code.
Bugs fixed: dartbug.com/36122
Change-Id: Id64429b8e808356ab19b96ef6faf48577ae962db
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96946
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Aart Bik <ajcbik@google.com>
diff --git a/runtime/bin/ffi_test_functions.cc b/runtime/bin/ffi_test_functions.cc
index 248020d..6dfa08d 100644
--- a/runtime/bin/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test_functions.cc
@@ -33,9 +33,60 @@
return retval;
}
-// Test 32-bit (int32_t) -> 64-bit (Dart int) sign extension and truncation.
-DART_EXPORT int32_t TestExtension() {
- return 1UL << 31;
+//// Tests for sign and zero extension of arguments and results.
+
+DART_EXPORT uint8_t ReturnMaxUint8() {
+ return 0xff;
+}
+
+DART_EXPORT uint16_t ReturnMaxUint16() {
+ return 0xffff;
+}
+
+DART_EXPORT uint32_t ReturnMaxUint32() {
+ return 0xffffffff;
+}
+
+DART_EXPORT int8_t ReturnMinInt8() {
+ return 0x80;
+}
+
+DART_EXPORT int16_t ReturnMinInt16() {
+ return 0x8000;
+}
+
+DART_EXPORT int32_t ReturnMinInt32() {
+ return 0x80000000;
+}
+
+DART_EXPORT intptr_t TakeMaxUint8(uint8_t x) {
+ return x == 0xff ? 1 : 0;
+}
+
+DART_EXPORT intptr_t TakeMaxUint16(uint16_t x) {
+ return x == 0xffff ? 1 : 0;
+}
+
+DART_EXPORT intptr_t TakeMaxUint32(uint32_t x) {
+ return x == 0xffffffff ? 1 : 0;
+}
+
+DART_EXPORT intptr_t TakeMinInt8(int8_t x) {
+ const int64_t expected = -0x80;
+ const int64_t received = x;
+ return expected == received ? 1 : 0;
+}
+
+DART_EXPORT intptr_t TakeMinInt16(int16_t x) {
+ const int64_t expected = -0x8000;
+ const int64_t received = x;
+ return expected == received ? 1 : 0;
+}
+
+DART_EXPORT intptr_t TakeMinInt32(int32_t x) {
+ const int64_t expected = -(int32_t)0x80000000;
+ const int64_t received = x;
+ return expected == received ? 1 : 0;
}
// Performs some computation on various sized signed ints.
diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc
index 7eab5b9..855e00a 100644
--- a/runtime/vm/compiler/backend/constant_propagator.cc
+++ b/runtime/vm/compiler/backend/constant_propagator.cc
@@ -1320,6 +1320,11 @@
SetValue(instr, non_constant_);
}
+void ConstantPropagator::VisitUnboxedWidthExtender(
+ UnboxedWidthExtenderInstr* instr) {
+ SetValue(instr, non_constant_);
+}
+
void ConstantPropagator::VisitUnaryUint32Op(UnaryUint32OpInstr* instr) {
// TODO(kmillikin): Handle unary operations.
SetValue(instr, non_constant_);
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index a16b035..d7ae5c0 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -450,6 +450,7 @@
M(BoxInt32, _) \
M(UnboxInt32, kNoGC) \
M(UnboxedIntConverter, _) \
+ M(UnboxedWidthExtender, _) \
M(Deoptimize, kNoGC) \
M(SimdOp, kNoGC)
@@ -7500,7 +7501,58 @@
DISALLOW_COPY_AND_ASSIGN(UnboxedIntConverterInstr);
};
+// Sign- or zero-extends an integer in unboxed 32-bit representation.
//
+// The choice between sign- and zero- extension is made based on the whether the
+// chosen representation is signed or unsigned.
+//
+// It is only supported to extend 1- or 2-byte operands; however, since we don't
+// have a representation less than 32-bits, both the input and output
+// representations are 32-bit (and equal).
+class UnboxedWidthExtenderInstr : public TemplateDefinition<1, NoThrow, Pure> {
+ public:
+ UnboxedWidthExtenderInstr(Value* value,
+ Representation rep,
+ intptr_t from_width_bytes)
+ : TemplateDefinition(DeoptId::kNone),
+ representation_(rep),
+ from_width_bytes_(from_width_bytes) {
+ ASSERT(from_width_bytes == 1 || from_width_bytes == 2);
+ ASSERT(rep == kUnboxedInt32 || rep == kUnboxedUint32);
+ SetInputAt(0, value);
+ }
+
+ Value* value() const { return inputs_[0]; }
+
+ Representation representation() const { return representation_; }
+
+ bool ComputeCanDeoptimize() const { return false; }
+
+ virtual Representation RequiredInputRepresentation(intptr_t idx) const {
+ ASSERT(idx == 0);
+ return representation_;
+ }
+
+ virtual bool AttributesEqual(Instruction* other) const {
+ ASSERT(other->IsUnboxedWidthExtender());
+ const UnboxedWidthExtenderInstr* ext = other->AsUnboxedWidthExtender();
+ return ext->representation() == representation() &&
+ ext->from_width_bytes_ == from_width_bytes_;
+ }
+
+ virtual CompileType ComputeType() const { return CompileType::Int(); }
+
+ DECLARE_INSTRUCTION(UnboxedWidthExtender);
+
+ PRINT_OPERANDS_TO_SUPPORT
+
+ private:
+ const Representation representation_;
+ const intptr_t from_width_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnboxedWidthExtenderInstr);
+};
+
// SimdOpInstr
//
// All SIMD intrinsics and recognized methods are represented via instances
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index bcf71bb..34bad43 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -6585,6 +6585,36 @@
}
}
+LocationSummary* UnboxedWidthExtenderInstr::MakeLocationSummary(
+ Zone* zone,
+ bool is_optimizing) const {
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary = new (zone)
+ LocationSummary(zone, /*num_inputs=*/InputCount(),
+ /*num_temps=*/kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RequiresRegister());
+ summary->set_out(0, Location::SameAsFirstInput());
+ return summary;
+}
+
+void UnboxedWidthExtenderInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ Register reg = locs()->in(0).reg();
+ // There are no builtin sign- or zero-extension instructions, so we'll have to
+ // use shifts instead.
+ const intptr_t shift_length = (kWordSize - from_width_bytes_) * kBitsPerByte;
+ __ Lsl(reg, reg, Operand(shift_length));
+ switch (representation_) {
+ case kUnboxedInt32: // Sign extend operand.
+ __ Asr(reg, reg, Operand(shift_length));
+ break;
+ case kUnboxedUint32: // Zero extend operand.
+ __ Lsr(reg, reg, Operand(shift_length));
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
LocationSummary* ThrowInstr::MakeLocationSummary(Zone* zone, bool opt) const {
return new (zone) LocationSummary(zone, 0, 0, LocationSummary::kCall);
}
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 7e1de69..6a4649d 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -5819,6 +5819,50 @@
}
}
+LocationSummary* UnboxedWidthExtenderInstr::MakeLocationSummary(
+ Zone* zone,
+ bool is_optimizing) const {
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary = new (zone)
+ LocationSummary(zone, /*num_inputs=*/InputCount(),
+ /*num_temps=*/kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RequiresRegister());
+ summary->set_out(0, Location::SameAsFirstInput());
+ return summary;
+}
+
+void UnboxedWidthExtenderInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ Register reg = locs()->in(0).reg();
+ switch (representation_) {
+ case kUnboxedInt32: // Sign extend operand.
+ switch (from_width_bytes_) {
+ case 1:
+ __ sxtb(reg, reg);
+ break;
+ case 2:
+ __ sxth(reg, reg);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case kUnboxedUint32: // Zero extend operand.
+ switch (from_width_bytes_) {
+ case 1:
+ __ uxtb(reg, reg);
+ break;
+ case 2:
+ __ uxth(reg, reg);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
LocationSummary* ThrowInstr::MakeLocationSummary(Zone* zone, bool opt) const {
return new (zone) LocationSummary(zone, 0, 0, LocationSummary::kCall);
}
diff --git a/runtime/vm/compiler/backend/il_dbc.cc b/runtime/vm/compiler/backend/il_dbc.cc
index 580ffee..27f7815 100644
--- a/runtime/vm/compiler/backend/il_dbc.cc
+++ b/runtime/vm/compiler/backend/il_dbc.cc
@@ -46,7 +46,8 @@
M(SpeculativeShiftUint32Op) \
M(TruncDivMod) \
M(UnaryUint32Op) \
- M(UnboxedIntConverter)
+ M(UnboxedIntConverter) \
+ M(UnboxedWidthExtender)
// List of instructions that are not used by DBC.
// Things we aren't planning to implement for DBC:
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index 1d0b013..a935736 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -5926,6 +5926,49 @@
}
}
+LocationSummary* UnboxedWidthExtenderInstr::MakeLocationSummary(
+ Zone* zone,
+ bool is_optimizing) const {
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary = new (zone)
+ LocationSummary(zone, /*num_inputs=*/InputCount(),
+ /*num_temps=*/kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RegisterLocation(EAX));
+ summary->set_out(0, Location::RegisterLocation(EAX));
+ return summary;
+}
+
+void UnboxedWidthExtenderInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ switch (representation_) {
+ case kUnboxedInt32: // Sign-extend operand.
+ switch (from_width_bytes_) {
+ case 1:
+ __ movsxb(EAX, AL);
+ break;
+ case 2:
+ __ movsxw(EAX, EAX);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case kUnboxedUint32: // Zero-extend operand.
+ switch (from_width_bytes_) {
+ case 1:
+ __ movzxb(EAX, AL);
+ break;
+ case 2:
+ __ movzxw(EAX, EAX);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
LocationSummary* ThrowInstr::MakeLocationSummary(Zone* zone, bool opt) const {
return new (zone) LocationSummary(zone, 0, 0, LocationSummary::kCall);
}
diff --git a/runtime/vm/compiler/backend/il_printer.cc b/runtime/vm/compiler/backend/il_printer.cc
index c6ab009..a329b13 100644
--- a/runtime/vm/compiler/backend/il_printer.cc
+++ b/runtime/vm/compiler/backend/il_printer.cc
@@ -991,6 +991,12 @@
Definition::PrintOperandsTo(f);
}
+void UnboxedWidthExtenderInstr::PrintOperandsTo(BufferFormatter* f) const {
+ f->Print("%" Pd " -> 4 (%s), ", from_width_bytes_,
+ RepresentationToCString(representation()));
+ Definition::PrintOperandsTo(f);
+}
+
void ParameterInstr::PrintOperandsTo(BufferFormatter* f) const {
f->Print("%" Pd, index());
}
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 53c0b02..89a8616 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -6207,6 +6207,49 @@
}
}
+LocationSummary* UnboxedWidthExtenderInstr::MakeLocationSummary(
+ Zone* zone,
+ bool is_optimizing) const {
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary = new (zone)
+ LocationSummary(zone, /*num_inputs=*/InputCount(),
+ /*num_temps=*/kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RegisterLocation(RAX));
+ summary->set_out(0, Location::RegisterLocation(RAX));
+ return summary;
+}
+
+void UnboxedWidthExtenderInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ switch (representation_) {
+ case kUnboxedInt32: // Sign extend operand.
+ switch (from_width_bytes_) {
+ case 1:
+ __ movsxb(RAX, RAX);
+ break;
+ case 2:
+ __ movsxw(RAX, RAX);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case kUnboxedUint32: // Zero extend operand.
+ switch (from_width_bytes_) {
+ case 1:
+ __ movzxb(RAX, RAX);
+ break;
+ case 2:
+ __ movzxw(RAX, RAX);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
LocationSummary* ThrowInstr::MakeLocationSummary(Zone* zone, bool opt) const {
return new (zone) LocationSummary(zone, 0, 0, LocationSummary::kCall);
}
diff --git a/runtime/vm/compiler/ffi.h b/runtime/vm/compiler/ffi.h
index e3eca6f..2cfaf78 100644
--- a/runtime/vm/compiler/ffi.h
+++ b/runtime/vm/compiler/ffi.h
@@ -18,6 +18,10 @@
namespace ffi {
+// On all supported platforms, the minimum width an argument must be sign- or
+// zero-extended to is 4 bytes.
+constexpr intptr_t kMinimumArgumentWidth = 4;
+
// Storage size for an FFI type (extends 'ffi.NativeType').
size_t ElementSizeInBytes(intptr_t class_id);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 15b989b..931dc0f 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -2419,6 +2419,18 @@
return Fragment(box);
}
+Fragment FlowGraphBuilder::FfiUnboxedExtend(Representation representation,
+ const AbstractType& ffi_type) {
+ const intptr_t width =
+ compiler::ffi::ElementSizeInBytes(ffi_type.type_class_id());
+ if (width >= compiler::ffi::kMinimumArgumentWidth) return {};
+
+ auto* extend =
+ new (Z) UnboxedWidthExtenderInstr(Pop(), representation, width);
+ Push(extend);
+ return Fragment(extend);
+}
+
Fragment FlowGraphBuilder::FfiPointerFromAddress(const Type& result_type) {
Fragment test;
TargetEntryInstr* null_entry;
@@ -2515,7 +2527,9 @@
body += LoadAddressFromFfiPointer();
body += UnboxTruncate(kUnboxedIntPtr);
} else {
- body += UnboxTruncate(arg_reps[pos - 1]);
+ Representation rep = arg_reps[pos - 1];
+ body += UnboxTruncate(rep);
+ body += FfiUnboxedExtend(rep, ffi_type);
}
}
@@ -2538,7 +2552,9 @@
body += Drop();
body += NullConstant();
} else {
- body += Box(compiler::ffi::ResultRepresentation(signature));
+ Representation rep = compiler::ffi::ResultRepresentation(signature);
+ body += FfiUnboxedExtend(rep, ffi_type);
+ body += Box(rep);
}
body += Return(TokenPosition::kNoSource);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index 1e76f9a..4355383 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -217,6 +217,11 @@
// Sign-extends kUnboxedInt32 and zero-extends kUnboxedUint32.
Fragment Box(Representation from);
+ // Sign- or zero-extends an integer parameter or return value for an FFI call
+ // as necessary.
+ Fragment FfiUnboxedExtend(Representation representation,
+ const AbstractType& ffi_type);
+
// Pops an 'ffi.Pointer' off the stack.
// If it's null, pushes 0.
// Otherwise pushes the address (in boxed representation).
diff --git a/tests/standalone_2/ffi/function_test.dart b/tests/standalone_2/ffi/function_test.dart
index d22f771..2baf086 100644
--- a/tests/standalone_2/ffi/function_test.dart
+++ b/tests/standalone_2/ffi/function_test.dart
@@ -55,10 +55,10 @@
}
typedef NativeQuadOpSigned = ffi.Int64 Function(
- ffi.Int64, ffi.Int32, ffi.Int16, ffi.Int8);
+ ffi.Int8, ffi.Int16, ffi.Int32, ffi.Int64);
typedef QuadOp = int Function(int, int, int, int);
typedef NativeQuadOpUnsigned = ffi.Uint64 Function(
- ffi.Uint64, ffi.Uint32, ffi.Uint16, ffi.Uint8);
+ ffi.Uint8, ffi.Uint16, ffi.Uint32, ffi.Uint64);
BinaryOp sumPlus42 =
ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
@@ -77,23 +77,92 @@
-0x8000000000000000, intComputation(0, 0, 0, -0x8000000000000000));
}
-typedef NativeNullaryOpSigned = ffi.Int32 Function();
-typedef NativeNullaryOpUnsigned = ffi.Uint32 Function();
-
-int Function() unsignedOp = ffiTestFunctions
- .lookup("TestExtension")
- .cast<ffi.Pointer<ffi.NativeFunction<NativeNullaryOpUnsigned>>>()
+typedef NativeReturnMaxUint8 = ffi.Uint8 Function();
+int Function() returnMaxUint8 = ffiTestFunctions
+ .lookup("ReturnMaxUint8")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeReturnMaxUint8>>>()
.asFunction();
-int Function() signedOp = ffiTestFunctions
- .lookup("TestExtension")
- .cast<ffi.Pointer<ffi.NativeFunction<NativeNullaryOpSigned>>>()
+typedef NativeReturnMaxUint16 = ffi.Uint16 Function();
+int Function() returnMaxUint16 = ffiTestFunctions
+ .lookup("ReturnMaxUint16")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeReturnMaxUint16>>>()
.asFunction();
-// Test 32-bit (int32_t) -> 64-bit (Dart int) sign extension and truncation.
+typedef NativeReturnMaxUint32 = ffi.Uint32 Function();
+int Function() returnMaxUint32 = ffiTestFunctions
+ .lookup("ReturnMaxUint32")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeReturnMaxUint32>>>()
+ .asFunction();
+
+typedef NativeReturnMinInt8 = ffi.Int8 Function();
+int Function() returnMinInt8 = ffiTestFunctions
+ .lookup("ReturnMinInt8")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeReturnMinInt8>>>()
+ .asFunction();
+
+typedef NativeReturnMinInt16 = ffi.Int16 Function();
+int Function() returnMinInt16 = ffiTestFunctions
+ .lookup("ReturnMinInt16")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeReturnMinInt16>>>()
+ .asFunction();
+
+typedef NativeReturnMinInt32 = ffi.Int32 Function();
+int Function() returnMinInt32 = ffiTestFunctions
+ .lookup("ReturnMinInt32")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeReturnMinInt32>>>()
+ .asFunction();
+
+typedef NativeTakeMaxUint8 = ffi.IntPtr Function(ffi.Uint8);
+int Function(int) takeMaxUint8 = ffiTestFunctions
+ .lookup("TakeMaxUint8")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeTakeMaxUint8>>>()
+ .asFunction();
+
+typedef NativeTakeMaxUint16 = ffi.IntPtr Function(ffi.Uint16);
+int Function(int) takeMaxUint16 = ffiTestFunctions
+ .lookup("TakeMaxUint16")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeTakeMaxUint16>>>()
+ .asFunction();
+
+typedef NativeTakeMaxUint32 = ffi.IntPtr Function(ffi.Uint32);
+int Function(int) takeMaxUint32 = ffiTestFunctions
+ .lookup("TakeMaxUint32")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeTakeMaxUint32>>>()
+ .asFunction();
+
+typedef NativeTakeMinInt8 = ffi.IntPtr Function(ffi.Int8);
+int Function(int) takeMinInt8 = ffiTestFunctions
+ .lookup("TakeMinInt8")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeTakeMinInt8>>>()
+ .asFunction();
+
+typedef NativeTakeMinInt16 = ffi.IntPtr Function(ffi.Int16);
+int Function(int) takeMinInt16 = ffiTestFunctions
+ .lookup("TakeMinInt16")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeTakeMinInt16>>>()
+ .asFunction();
+
+typedef NativeTakeMinInt32 = ffi.IntPtr Function(ffi.Int32);
+int Function(int) takeMinInt32 = ffiTestFunctions
+ .lookup("TakeMinInt32")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeTakeMinInt32>>>()
+ .asFunction();
+
void testExtension() {
- Expect.equals(unsignedOp(), 0x80000000);
- Expect.equals(signedOp(), 0xffffffff80000000);
+ Expect.equals(returnMaxUint8(), 0xff);
+ Expect.equals(returnMaxUint16(), 0xffff);
+ Expect.equals(returnMaxUint32(), 0xffffffff);
+ Expect.equals(returnMinInt8(), -0x80);
+ Expect.equals(returnMinInt16(), -0x8000);
+ Expect.equals(returnMinInt32(), -0x80000000);
+
+ Expect.equals(takeMaxUint8(0xff), 1);
+ Expect.equals(takeMaxUint16(0xffff), 1);
+ Expect.equals(takeMaxUint32(0xffffffff), 1);
+ Expect.equals(takeMinInt8(0x80), 1);
+ Expect.equals(takeMinInt16(0x8000), 1);
+ Expect.equals(takeMinInt32(0x80000000), 1);
}
QuadOp uintComputation = ffiTestFunctions
@@ -118,8 +187,6 @@
.lookupFunction<NativeSenaryOp, SenaryOp>("SumSmallNumbers");
void testTruncation() {
- // TODO(dacoharkes): implement truncation and sign extension in trampolines
- // for values smaller than 32 bits.
sumSmallNumbers(128, 0, 0, 0, 0, 0);
sumSmallNumbers(-129, 0, 0, 0, 0, 0);
sumSmallNumbers(0, 0, 0, 256, 0, 0);