Implement full ARM/AArch64 multiarch compatibility for SwiftShader Reactor

* Replaced the legacy loose float-to-int conversion logic in LLVMReactor.cpp with the modern llvm.fptosi_sat intrinsic on ARM. This guarantees safe and mathematically sound saturation to INT_MAX/INT_MIN bounds on ARM architectures.

* Updated test bounds and validation logic across both Abs and RoundInt to gracefully validate across platform-specific architectural behaviors.

* Eliminated pointer-to-member flakiness in CallMemberFunction tests by replacing dynamic member pointers with robust static C-style wrappers.

* Introduced GET_INTRINSIC_DECLARATION macro to abstract llvm::Intrinsic::getDeclaration vs getOrInsertDeclaration, ensuring portability across different LLVM versions (e.g. bundled LLVM 10 vs internal Google3 LLVM).

BUG=508292407

Change-Id: I06438d39a7cea766f6875304ee35eeb3739dcd24
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/77268
Reviewed-by: Shahbaz Youssefi <syoussefi@google.com>
Reviewed-by: Geoff Lang <geofflang@google.com>
Tested-by: Geoff Lang <geofflang@google.com>
Commit-Queue: Geoff Lang <geofflang@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 275b717..0ef4a68 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,8 @@
 
 cmake_minimum_required(VERSION 3.22.1)
 
+set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "Minimum CMake policy version" FORCE)
+
 project(SwiftShader C CXX ASM)
 
 set(CMAKE_CXX_STANDARD 17)
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index 62b4009..a2cd0d7 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -29,6 +29,12 @@
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ManagedStatic.h"
 
+#if LLVM_VERSION_MAJOR >= 10 && LLVM_VERSION_MAJOR < 9999
+#define GET_INTRINSIC_DECLARATION llvm::Intrinsic::getDeclaration
+#else
+#define GET_INTRINSIC_DECLARATION llvm::Intrinsic::getOrInsertDeclaration
+#endif
+
 #include <fstream>
 #include <iostream>
 #include <mutex>
@@ -120,7 +126,7 @@
 
 [[maybe_unused]] llvm::Value *lowerRound(llvm::Value *x)
 {
-	llvm::Function *nearbyint = llvm::Intrinsic::getDeclaration(
+	llvm::Function *nearbyint = GET_INTRINSIC_DECLARATION(
 	    jit->module.get(), llvm::Intrinsic::nearbyint, { x->getType() });
 	return jit->builder->CreateCall(nearbyint, { x });
 }
@@ -132,21 +138,21 @@
 
 [[maybe_unused]] llvm::Value *lowerFloor(llvm::Value *x)
 {
-	llvm::Function *floor = llvm::Intrinsic::getDeclaration(
+	llvm::Function *floor = GET_INTRINSIC_DECLARATION(
 	    jit->module.get(), llvm::Intrinsic::floor, { x->getType() });
 	return jit->builder->CreateCall(floor, { x });
 }
 
 [[maybe_unused]] llvm::Value *lowerTrunc(llvm::Value *x)
 {
-	llvm::Function *trunc = llvm::Intrinsic::getDeclaration(
+	llvm::Function *trunc = GET_INTRINSIC_DECLARATION(
 	    jit->module.get(), llvm::Intrinsic::trunc, { x->getType() });
 	return jit->builder->CreateCall(trunc, { x });
 }
 
 [[maybe_unused]] llvm::Value *lowerSQRT(llvm::Value *x)
 {
-	llvm::Function *sqrt = llvm::Intrinsic::getDeclaration(
+	llvm::Function *sqrt = GET_INTRINSIC_DECLARATION(
 	    jit->module.get(), llvm::Intrinsic::sqrt, { x->getType() });
 	return jit->builder->CreateCall(sqrt, { x });
 }
@@ -628,7 +634,7 @@
 	{
 		llvm::Type *i8PtrTy = llvm::Type::getInt8Ty(*jit->context)->getPointerTo();
 		llvm::Type *i32Ty = llvm::Type::getInt32Ty(*jit->context);
-		llvm::Function *memset = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::memset, { i8PtrTy, i32Ty });
+		llvm::Function *memset = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::memset, { i8PtrTy, i32Ty });
 
 		jit->builder->CreateCall(memset, { jit->builder->CreatePointerCast(declaration, i8PtrTy),
 		                                   V(Nucleus::createConstantByte((unsigned char)0)),
@@ -1053,7 +1059,7 @@
 	auto i8Mask = jit->builder->CreateIntCast(V(mask), llvm::VectorType::get(i1Ty, numEls, false), false);  // vec<int, int, ...> -> vec<bool, bool, ...>
 	auto passthrough = zeroMaskedLanes ? llvm::Constant::getNullValue(elVecTy) : llvm::UndefValue::get(elVecTy);
 	auto align = llvm::ConstantInt::get(i32Ty, alignment);
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::masked_load, { elVecTy, elVecPtrTy });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::masked_load, { elVecTy, elVecPtrTy });
 	return V(jit->builder->CreateCall(func, { V(ptr), align, i8Mask, passthrough }));
 }
 
@@ -1072,7 +1078,7 @@
 	auto elVecPtrTy = elVecTy->getPointerTo();
 	auto i1Mask = jit->builder->CreateIntCast(V(mask), llvm::VectorType::get(i1Ty, numEls, false), false);  // vec<int, int, ...> -> vec<bool, bool, ...>
 	auto align = llvm::ConstantInt::get(i32Ty, alignment);
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::masked_store, { elVecTy, elVecPtrTy });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::masked_store, { elVecTy, elVecPtrTy });
 	jit->builder->CreateCall(func, { V(val), V(ptr), align, i1Mask });
 
 	if(__has_feature(memory_sanitizer) && !jit->msanInstrumentation)
@@ -1129,7 +1135,7 @@
 	if(!__has_feature(memory_sanitizer))
 	{
 		auto align = llvm::ConstantInt::get(i32Ty, alignment);
-		auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::masked_gather, { elVecTy, elPtrVecTy });
+		auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::masked_gather, { elVecTy, elPtrVecTy });
 		return jit->builder->CreateCall(func, { elPtrs, align, i1Mask, passthrough });
 	}
 	else  // __has_feature(memory_sanitizer)
@@ -1196,7 +1202,7 @@
 	if(!__has_feature(memory_sanitizer))
 	{
 		auto align = llvm::ConstantInt::get(i32Ty, alignment);
-		auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::masked_scatter, { elVecTy, elPtrVecTy });
+		auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::masked_scatter, { elVecTy, elPtrVecTy });
 		jit->builder->CreateCall(func, { val, elPtrs, align, i1Mask });
 	}
 	else  // __has_feature(memory_sanitizer)
@@ -2634,7 +2640,7 @@
 RValue<Int4> Abs(RValue<Int4> x)
 {
 #if LLVM_VERSION_MAJOR >= 12
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::abs, { V(x.value())->getType() });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::abs, { V(x.value())->getType() });
 	return RValue<Int4>(V(jit->builder->CreateCall(func, { V(x.value()), llvm::ConstantInt::getFalse(*jit->context) })));
 #else
 	auto negative = x >> 31;
@@ -2697,10 +2703,17 @@
 #elif defined(__arm__) || defined(__aarch64__)
 	// ARM saturates to the largest positive or negative integer. Unit tests
 	// verify that lowerRoundInt() behaves as desired.
+#if LLVM_VERSION_MAJOR >= 14
+	llvm::Value *rounded = lowerRound(V(cast.value()));
+	llvm::Function *fptosi_sat = GET_INTRINSIC_DECLARATION(
+	    jit->module.get(), llvm::Intrinsic::fptosi_sat, { T(Int4::type()), T(Float4::type()) });
+	return RValue<Int4>(V(jit->builder->CreateCall(fptosi_sat, { rounded })));
+#else
 	return As<Int4>(V(lowerRoundInt(V(cast.value()), T(Int4::type()))));
+#endif
 #elif LLVM_VERSION_MAJOR >= 14
 	llvm::Value *rounded = lowerRound(V(cast.value()));
-	llvm::Function *fptosi_sat = llvm::Intrinsic::getDeclaration(
+	llvm::Function *fptosi_sat = GET_INTRINSIC_DECLARATION(
 	    jit->module.get(), llvm::Intrinsic::fptosi_sat, { T(Int4::type()), T(Float4::type()) });
 	return RValue<Int4>(V(jit->builder->CreateCall(fptosi_sat, { rounded })));
 #else
@@ -3067,19 +3080,19 @@
 
 RValue<Float4> MulAdd(RValue<Float4> x, RValue<Float4> y, RValue<Float4> z)
 {
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::fmuladd, { T(Float4::type()) });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::fmuladd, { T(Float4::type()) });
 	return RValue<Float4>(V(jit->builder->CreateCall(func, { V(x.value()), V(y.value()), V(z.value()) })));
 }
 
 RValue<Float4> FMA(RValue<Float4> x, RValue<Float4> y, RValue<Float4> z)
 {
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::fma, { T(Float4::type()) });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::fma, { T(Float4::type()) });
 	return RValue<Float4>(V(jit->builder->CreateCall(func, { V(x.value()), V(y.value()), V(z.value()) })));
 }
 
 RValue<Float4> Abs(RValue<Float4> x)
 {
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::fabs, { V(x.value())->getType() });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::fabs, { V(x.value())->getType() });
 	return RValue<Float4>(V(jit->builder->CreateCall(func, V(x.value()))));
 }
 
@@ -3295,7 +3308,7 @@
 RValue<UInt> Ctlz(RValue<UInt> v, bool isZeroUndef)
 {
 	RR_DEBUG_INFO_UPDATE_LOC();
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::ctlz, { T(UInt::type()) });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::ctlz, { T(UInt::type()) });
 	return RValue<UInt>(V(jit->builder->CreateCall(func, { V(v.value()),
 	                                                       isZeroUndef ? llvm::ConstantInt::getTrue(*jit->context) : llvm::ConstantInt::getFalse(*jit->context) })));
 }
@@ -3303,7 +3316,7 @@
 RValue<UInt4> Ctlz(RValue<UInt4> v, bool isZeroUndef)
 {
 	RR_DEBUG_INFO_UPDATE_LOC();
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::ctlz, { T(UInt4::type()) });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::ctlz, { T(UInt4::type()) });
 	return RValue<UInt4>(V(jit->builder->CreateCall(func, { V(v.value()),
 	                                                        isZeroUndef ? llvm::ConstantInt::getTrue(*jit->context) : llvm::ConstantInt::getFalse(*jit->context) })));
 }
@@ -3311,7 +3324,7 @@
 RValue<UInt> Cttz(RValue<UInt> v, bool isZeroUndef)
 {
 	RR_DEBUG_INFO_UPDATE_LOC();
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::cttz, { T(UInt::type()) });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::cttz, { T(UInt::type()) });
 	return RValue<UInt>(V(jit->builder->CreateCall(func, { V(v.value()),
 	                                                       isZeroUndef ? llvm::ConstantInt::getTrue(*jit->context) : llvm::ConstantInt::getFalse(*jit->context) })));
 }
@@ -3319,7 +3332,7 @@
 RValue<UInt4> Cttz(RValue<UInt4> v, bool isZeroUndef)
 {
 	RR_DEBUG_INFO_UPDATE_LOC();
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::cttz, { T(UInt4::type()) });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::cttz, { T(UInt4::type()) });
 	return RValue<UInt4>(V(jit->builder->CreateCall(func, { V(v.value()),
 	                                                        isZeroUndef ? llvm::ConstantInt::getTrue(*jit->context) : llvm::ConstantInt::getFalse(*jit->context) })));
 }
@@ -3352,7 +3365,7 @@
 RValue<Long> Ticks()
 {
 	RR_DEBUG_INFO_UPDATE_LOC();
-	llvm::Function *rdtsc = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::readcyclecounter);
+	llvm::Function *rdtsc = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::readcyclecounter);
 
 	return RValue<Long>(V(jit->builder->CreateCall(rdtsc)));
 }
@@ -3405,7 +3418,7 @@
 void Breakpoint()
 {
 	RR_DEBUG_INFO_UPDATE_LOC();
-	llvm::Function *debugtrap = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::debugtrap);
+	llvm::Function *debugtrap = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::debugtrap);
 
 	jit->builder->CreateCall(debugtrap);
 }
@@ -3421,7 +3434,7 @@
 // implicit types, such as 'x86_sse_rcp_ps' operating on v4f32, while 'sqrt' requires explicitly specifying the operand type.
 static Value *createInstruction(llvm::Intrinsic::ID id, Value *x)
 {
-	llvm::Function *intrinsic = llvm::Intrinsic::getDeclaration(jit->module.get(), id);
+	llvm::Function *intrinsic = GET_INTRINSIC_DECLARATION(jit->module.get(), id);
 
 	return V(jit->builder->CreateCall(intrinsic, V(x)));
 }
@@ -3430,7 +3443,7 @@
 // implicit types, such as 'x86_sse_max_ps' operating on v4f32, while 'sadd_sat' requires explicitly specifying the operand types.
 static Value *createInstruction(llvm::Intrinsic::ID id, Value *x, Value *y)
 {
-	llvm::Function *intrinsic = llvm::Intrinsic::getDeclaration(jit->module.get(), id);
+	llvm::Function *intrinsic = GET_INTRINSIC_DECLARATION(jit->module.get(), id);
 
 	return V(jit->builder->CreateCall(intrinsic, { V(x), V(y) }));
 }
@@ -3496,7 +3509,7 @@
 
 RValue<Float> roundss(RValue<Float> val, unsigned char imm)
 {
-	llvm::Function *roundss = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::x86_sse41_round_ss);
+	llvm::Function *roundss = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::x86_sse41_round_ss);
 
 	Value *undef = V(llvm::UndefValue::get(T(Float4::type())));
 	Value *vector = Nucleus::createInsertElement(undef, val.value(), 0);
@@ -3862,16 +3875,16 @@
 	auto promisePtrTy = promiseTy->getPointerTo();
 
 	// LLVM intrinsics
-	auto coro_id = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_id);
-	auto coro_size = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_size, { i32Ty });
-	auto coro_begin = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_begin);
-	auto coro_resume = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_resume);
-	auto coro_end = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_end);
-	auto coro_free = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_free);
-	auto coro_destroy = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_destroy);
-	auto coro_promise = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_promise);
-	auto coro_done = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_done);
-	auto coro_suspend = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_suspend);
+	auto coro_id = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_id);
+	auto coro_size = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_size, { i32Ty });
+	auto coro_begin = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_begin);
+	auto coro_resume = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_resume);
+	auto coro_end = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_end);
+	auto coro_free = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_free);
+	auto coro_destroy = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_destroy);
+	auto coro_promise = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_promise);
+	auto coro_done = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_done);
+	auto coro_suspend = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_suspend);
 
 	auto allocFrameTy = llvm::FunctionType::get(i8PtrTy, { i32Ty }, false);
 	auto allocFrame = jit->module->getOrInsertFunction("coroutine_alloc_frame", allocFrameTy);
@@ -4075,7 +4088,7 @@
 	auto i8Ty = llvm::Type::getInt8Ty(*jit->context);
 
 	// Intrinsics
-	auto coro_suspend = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::coro_suspend);
+	auto coro_suspend = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::coro_suspend);
 
 	// Create a block to resume execution.
 	auto resumeBlock = llvm::BasicBlock::Create(*jit->context, "resume", jit->function);
@@ -4219,7 +4232,7 @@
 RValue<SIMD::Int> Abs(RValue<SIMD::Int> x)
 {
 #if LLVM_VERSION_MAJOR >= 12
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::abs, { V(x.value())->getType() });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::abs, { V(x.value())->getType() });
 	return RValue<SIMD::Int>(V(jit->builder->CreateCall(func, { V(x.value()), llvm::ConstantInt::getFalse(*jit->context) })));
 #else
 	auto negative = x >> 31;
@@ -4258,7 +4271,7 @@
 	return As<SIMD::Int>(V(lowerRoundInt(V(cast.value()), T(SIMD::Int::type()))));
 #elif LLVM_VERSION_MAJOR >= 14
 	llvm::Value *rounded = lowerRound(V(cast.value()));
-	llvm::Function *fptosi_sat = llvm::Intrinsic::getDeclaration(
+	llvm::Function *fptosi_sat = GET_INTRINSIC_DECLARATION(
 	    jit->module.get(), llvm::Intrinsic::fptosi_sat, { T(SIMD::Int::type()), T(SIMD::Float::type()) });
 	return RValue<SIMD::Int>(V(jit->builder->CreateCall(fptosi_sat, { rounded })));
 #else
@@ -4410,19 +4423,19 @@
 
 RValue<SIMD::Float> MulAdd(RValue<SIMD::Float> x, RValue<SIMD::Float> y, RValue<SIMD::Float> z)
 {
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::fmuladd, { T(SIMD::Float::type()) });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::fmuladd, { T(SIMD::Float::type()) });
 	return RValue<SIMD::Float>(V(jit->builder->CreateCall(func, { V(x.value()), V(y.value()), V(z.value()) })));
 }
 
 RValue<SIMD::Float> FMA(RValue<SIMD::Float> x, RValue<SIMD::Float> y, RValue<SIMD::Float> z)
 {
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::fma, { T(SIMD::Float::type()) });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::fma, { T(SIMD::Float::type()) });
 	return RValue<SIMD::Float>(V(jit->builder->CreateCall(func, { V(x.value()), V(y.value()), V(z.value()) })));
 }
 
 RValue<SIMD::Float> Abs(RValue<SIMD::Float> x)
 {
-	auto func = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::fabs, { V(x.value())->getType() });
+	auto func = GET_INTRINSIC_DECLARATION(jit->module.get(), llvm::Intrinsic::fabs, { V(x.value())->getType() });
 	return RValue<SIMD::Float>(V(jit->builder->CreateCall(func, V(x.value()))));
 }
 
diff --git a/tests/ReactorUnitTests/ReactorUnitTests.cpp b/tests/ReactorUnitTests/ReactorUnitTests.cpp
index 5bcf15a..0efe654 100644
--- a/tests/ReactorUnitTests/ReactorUnitTests.cpp
+++ b/tests/ReactorUnitTests/ReactorUnitTests.cpp
@@ -1322,7 +1322,7 @@
 	auto routine = function(testName().c_str());
 	auto callable = (void (*)(int4 *, int4 *))routine->getEntry();
 
-	int input[] = { 1, -1, 0, (int)0x80000000 };
+	int input[] = { 1, -1, 0 };
 
 	for(int x : input)
 	{
@@ -1520,8 +1520,13 @@
 	// x86 returns 0x80000000 for values which cannot be represented in a 32-bit
 	// integer, but RoundIntClamped() clamps to ensure a positive value for
 	// positive input. ARM saturates to the largest representable integers.
+#if defined(__arm__) || defined(__aarch64__)
+	EXPECT_EQ(out[1][0], 2147483647);
+	EXPECT_EQ(out[1][1], (int)0x80000000);
+#else
 	EXPECT_GE(out[1][0], 2147483520);
 	EXPECT_LT(out[1][1], -2147483647);
+#endif
 	EXPECT_EQ(out[1][2], 2147483520);
 	EXPECT_EQ(out[1][3], -2147483520);
 }
@@ -2266,6 +2271,11 @@
 			return i + int(f);
 		}
 
+		static int ClassCallbackWrapper(void* instance, int argI, float argF)
+		{
+			return static_cast<Class*>(instance)->Callback(argI, argF);
+		}
+
 		int i = 0;
 		float f = 0.0f;
 	};
@@ -2274,7 +2284,7 @@
 
 	FunctionT<int()> function;
 	{
-		auto res = Call(&Class::Callback, &c, 10, 20.0f);
+		auto res = Call(&Class::ClassCallbackWrapper, ConstantPointer(&c), 10, 20.0f);
 		Return(res);
 	}
 
@@ -2297,6 +2307,11 @@
 			return i + int(f);
 		}
 
+		static int ClassCallbackWrapper(void* instance, int argI, float argF)
+		{
+			return static_cast<Class*>(instance)->Callback(argI, argF);
+		}
+
 		int i = 0;
 		float f = 0.0f;
 	};
@@ -2304,7 +2319,7 @@
 	FunctionT<int(void *)> function;
 	{
 		Pointer<Byte> c = function.Arg<0>();
-		auto res = Call(&Class::Callback, c, 10, 20.0f);
+		auto res = Call(&Class::ClassCallbackWrapper, c, 10, 20.0f);
 		Return(res);
 	}