Reactor: Add support for calling C functions. Bug: b/130746922 Change-Id: I0255460280d126e471dea107ffe976a1d765a218 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29335 Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Tested-by: Ben Clayton <bclayton@google.com> Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp index fc9f5e9..9c453b7 100644 --- a/src/Reactor/LLVMReactor.cpp +++ b/src/Reactor/LLVMReactor.cpp
@@ -3271,6 +3271,28 @@ return RValue<Long>(V(::builder->CreateCall(rdtsc))); } + + RValue<Pointer<Byte>> ConstantPointer(void const * ptr) + { + // Note: this should work for 32-bit pointers as well because 'inttoptr' + // is defined to truncate (and zero extend) if necessary. + auto ptrAsInt = ::llvm::ConstantInt::get(::llvm::Type::getInt64Ty(*::context), reinterpret_cast<uintptr_t>(ptr)); + return RValue<Pointer<Byte>>(V(::builder->CreateIntToPtr(ptrAsInt, T(Pointer<Byte>::getType())))); + } + + Value* Call(RValue<Pointer<Byte>> fptr, Type* retTy, std::initializer_list<Value*> args, std::initializer_list<Type*> argTys) + { + ::llvm::SmallVector<::llvm::Type*, 8> paramTys; + for (auto ty : argTys) { paramTys.push_back(T(ty)); } + auto funcTy = ::llvm::FunctionType::get(T(retTy), paramTys, false); + + auto funcPtrTy = funcTy->getPointerTo(); + auto funcPtr = ::builder->CreatePointerCast(V(fptr.value), funcPtrTy); + + ::llvm::SmallVector<::llvm::Value*, 8> arguments; + for (auto arg : args) { arguments.push_back(V(arg)); } + return V(::builder->CreateCall(funcPtr, arguments)); + } } namespace rr
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp index e6f3ff2..b7dcaf3 100644 --- a/src/Reactor/Reactor.hpp +++ b/src/Reactor/Reactor.hpp
@@ -2959,6 +2959,97 @@ return ReinterpretCast<T>(val); } + template <typename T> + inline Value* valueOf(RValue<T> v) { return v.value; } + + template <typename T> + inline Value* valueOf(LValue<T> v) { return valueOf(RValue<T>(v.loadValue())); } + + template<typename T> + struct CToReactor; + + template<> struct CToReactor<void> { using type = Void; }; + template<> struct CToReactor<int> { using type = Int; }; + template<> struct CToReactor<float> { using type = Float; }; + template<> struct CToReactor<int*> { using type = Pointer<Int>; }; + template<> struct CToReactor<float*> { using type = Pointer<Float>; }; + + // Pointers to non-reactor types are treated as uint8_t*. + template<typename T> + struct CToReactor<T*> { using type = Pointer<Byte>; }; + + // Returns a reactor pointer to the fixed-address ptr. + RValue<Pointer<Byte>> ConstantPointer(void const * ptr); + + // Calls the function pointer fptr with the given arguments, return type + // and parameter types. Returns the call's return value if the function has + // a non-void return type. + Value* Call(RValue<Pointer<Byte>> fptr, Type* retTy, std::initializer_list<Value*> args, std::initializer_list<Type*> paramTys); + + template <typename F> + class CallHelper {}; + + template<typename Return, typename ... Arguments> + class CallHelper<Return(Arguments...)> + { + public: + using RReturn = typename CToReactor<Return>::type; + + static inline RReturn Call(Return(fptr)(Arguments...), typename CToReactor<Arguments>::type... args) + { + return RValue<RReturn>(rr::Call( + ConstantPointer(reinterpret_cast<void*>(fptr)), + RReturn::getType(), + { valueOf(args) ... }, + { CToReactor<Arguments>::type::getType() ... })); + } + + static inline RReturn Call(Pointer<Byte> fptr, typename CToReactor<Arguments>::type... args) + { + return RValue<RReturn>(rr::Call( + fptr, + RReturn::getType(), + { valueOf(args) ... }, + { CToReactor<Arguments>::type::getType() ... })); + } + }; + + template<typename ... Arguments> + class CallHelper<void(Arguments...)> + { + public: + static inline void Call(void(fptr)(Arguments...), typename CToReactor<Arguments>::type... args) + { + rr::Call(ConstantPointer(reinterpret_cast<void*>(fptr)), + Void::getType(), + { valueOf(args) ... }, + { CToReactor<Arguments>::type::getType() ... }); + } + + static inline void Call(Pointer<Byte> fptr, typename CToReactor<Arguments>::type... args) + { + rr::Call(fptr, + Void::getType(), + { valueOf(args) ... }, + { CToReactor<Arguments>::type::getType() ... }); + } + }; + + // Calls the function pointer fptr with the given arguments args. + template<typename Return, typename ... Arguments> + inline typename CToReactor<Return>::type Call(Return(fptr)(Arguments...), typename CToReactor<Arguments>::type... args) + { + return CallHelper<Return(Arguments...)>::Call(fptr, args...); + } + + // Calls the function pointer fptr with the signature FUNCTION_SIGNATURE and + // arguments. + template<typename FUNCTION_SIGNATURE, typename ... Arguments> + inline void Call(Pointer<Byte> fptr, Arguments ... args) + { + CallHelper<FUNCTION_SIGNATURE>::Call(fptr, args...); + } + #ifdef ENABLE_RR_PRINT // PrintValue holds the printf format and value(s) for a single argument // to Print(). A single argument can be expanded into multiple printf
diff --git a/src/Reactor/ReactorUnitTests.cpp b/src/Reactor/ReactorUnitTests.cpp index a5de4f9..b52b722 100644 --- a/src/Reactor/ReactorUnitTests.cpp +++ b/src/Reactor/ReactorUnitTests.cpp
@@ -1088,6 +1088,57 @@ delete routine; } +TEST(ReactorUnitTests, Call) +{ + if (!rr::Caps.CallSupported) + { + SUCCEED() << "rr::Call() not supported"; + return; + } + + Routine *routine = nullptr; + + struct Class + { + static int Callback(uint8_t *p, int i, float f) + { + auto c = reinterpret_cast<Class*>(p); + c->i = i; + c->f = f; + return i + int(f); + } + + int i = 0; + float f = 0.0f; + }; + + { + Function<Int(Pointer<Byte>)> function; + { + Pointer<Byte> c = function.Arg<0>(); + auto res = Call(Class::Callback, c, 10, 20.0f); + Return(res); + } + + routine = function("one"); + + if(routine) + { + int(*callable)(void*) = (int(*)(void*))routine->getEntry(); + + Class c; + + int res = callable(&c); + + EXPECT_EQ(res, 30); + EXPECT_EQ(c.i, 10); + EXPECT_EQ(c.f, 20.0f); + } + } + + delete routine; +} + // Check that a complex generated function which utilizes all 8 or 16 XMM // registers computes the correct result. // (Note that due to MSC's lack of support for inline assembly in x64,
diff --git a/src/Reactor/SubzeroReactor.cpp b/src/Reactor/SubzeroReactor.cpp index c30a419..731e682 100644 --- a/src/Reactor/SubzeroReactor.cpp +++ b/src/Reactor/SubzeroReactor.cpp
@@ -3381,6 +3381,28 @@ return Long(Int(0)); } + RValue<Pointer<Byte>> ConstantPointer(void const * ptr) + { + return RValue<Pointer<Byte>>(V(::context->getConstantInt64(reinterpret_cast<intptr_t>(ptr)))); + } + + Value* Call(RValue<Pointer<Byte>> fptr, Type* retTy, std::initializer_list<Value*> args, std::initializer_list<Type*> argTys) + { + // FIXME: This does not currently work on Windows. + Ice::Variable *ret = nullptr; + if (retTy != nullptr) + { + ret = ::function->makeVariable(T(retTy)); + } + auto call = Ice::InstCall::create(::function, args.size(), ret, V(fptr.value), false); + for (auto arg : args) + { + call->addArg(V(arg)); + } + ::basicBlock->appendInst(call); + return V(ret); + } + // Below are functions currently unimplemented for the Subzero backend. // They are stubbed to satisfy the linker. RValue<Float4> Sin(RValue<Float4> x) { UNIMPLEMENTED("Subzero Sin()"); return Float4(0); }