| // Copyright 2014 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_CCTEST_COMPILER_CALL_TESTER_H_ |
| #define V8_CCTEST_COMPILER_CALL_TESTER_H_ |
| |
| #include "src/v8.h" |
| |
| #include "src/simulator.h" |
| |
| #if V8_TARGET_ARCH_IA32 |
| #if __GNUC__ |
| #define V8_CDECL __attribute__((cdecl)) |
| #else |
| #define V8_CDECL __cdecl |
| #endif |
| #else |
| #define V8_CDECL |
| #endif |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| // TODO(titzer): use c-signature.h instead of ReturnValueTraits |
| template <typename R> |
| struct ReturnValueTraits { |
| static R Cast(uintptr_t r) { return reinterpret_cast<R>(r); } |
| static MachineType Representation() { |
| // TODO(dcarney): detect when R is of a subclass of Object* instead of this |
| // type check. |
| while (false) { |
| *(static_cast<Object* volatile*>(0)) = static_cast<R>(0); |
| } |
| return kMachAnyTagged; |
| } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<int32_t*> { |
| static int32_t* Cast(uintptr_t r) { return reinterpret_cast<int32_t*>(r); } |
| static MachineType Representation() { return kMachPtr; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<void> { |
| static void Cast(uintptr_t r) {} |
| static MachineType Representation() { return kMachPtr; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<bool> { |
| static bool Cast(uintptr_t r) { return static_cast<bool>(r); } |
| static MachineType Representation() { return kRepBit; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<int32_t> { |
| static int32_t Cast(uintptr_t r) { return static_cast<int32_t>(r); } |
| static MachineType Representation() { return kMachInt32; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<uint32_t> { |
| static uint32_t Cast(uintptr_t r) { return static_cast<uint32_t>(r); } |
| static MachineType Representation() { return kMachUint32; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<int64_t> { |
| static int64_t Cast(uintptr_t r) { return static_cast<int64_t>(r); } |
| static MachineType Representation() { return kMachInt64; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<uint64_t> { |
| static uint64_t Cast(uintptr_t r) { return static_cast<uint64_t>(r); } |
| static MachineType Representation() { return kMachUint64; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<int16_t> { |
| static int16_t Cast(uintptr_t r) { return static_cast<int16_t>(r); } |
| static MachineType Representation() { return kMachInt16; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<uint16_t> { |
| static uint16_t Cast(uintptr_t r) { return static_cast<uint16_t>(r); } |
| static MachineType Representation() { return kMachUint16; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<int8_t> { |
| static int8_t Cast(uintptr_t r) { return static_cast<int8_t>(r); } |
| static MachineType Representation() { return kMachInt8; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<uint8_t> { |
| static uint8_t Cast(uintptr_t r) { return static_cast<uint8_t>(r); } |
| static MachineType Representation() { return kMachUint8; } |
| }; |
| |
| template <> |
| struct ReturnValueTraits<double> { |
| static double Cast(uintptr_t r) { |
| UNREACHABLE(); |
| return 0.0; |
| } |
| static MachineType Representation() { return kMachFloat64; } |
| }; |
| |
| |
| template <typename R> |
| struct ParameterTraits { |
| static uintptr_t Cast(R r) { return static_cast<uintptr_t>(r); } |
| }; |
| |
| template <> |
| struct ParameterTraits<int*> { |
| static uintptr_t Cast(int* r) { return reinterpret_cast<uintptr_t>(r); } |
| }; |
| |
| template <typename T> |
| struct ParameterTraits<T*> { |
| static uintptr_t Cast(void* r) { return reinterpret_cast<uintptr_t>(r); } |
| }; |
| |
| class CallHelper { |
| public: |
| explicit CallHelper(Isolate* isolate, MachineSignature* machine_sig) |
| : machine_sig_(machine_sig), isolate_(isolate) { |
| USE(isolate_); |
| } |
| virtual ~CallHelper() {} |
| |
| static MachineSignature* MakeMachineSignature( |
| Zone* zone, MachineType return_type, MachineType p0 = kMachNone, |
| MachineType p1 = kMachNone, MachineType p2 = kMachNone, |
| MachineType p3 = kMachNone, MachineType p4 = kMachNone) { |
| // Count the number of parameters. |
| size_t param_count = 5; |
| MachineType types[] = {p0, p1, p2, p3, p4}; |
| while (param_count > 0 && types[param_count - 1] == kMachNone) |
| param_count--; |
| size_t return_count = return_type == kMachNone ? 0 : 1; |
| |
| // Build the machine signature. |
| MachineSignature::Builder builder(zone, return_count, param_count); |
| if (return_count > 0) builder.AddReturn(return_type); |
| for (size_t i = 0; i < param_count; i++) { |
| builder.AddParam(types[i]); |
| } |
| return builder.Build(); |
| } |
| |
| protected: |
| MachineSignature* machine_sig_; |
| void VerifyParameters(size_t parameter_count, MachineType* parameter_types) { |
| CHECK(machine_sig_->parameter_count() == parameter_count); |
| for (size_t i = 0; i < parameter_count; i++) { |
| CHECK_EQ(machine_sig_->GetParam(i), parameter_types[i]); |
| } |
| } |
| virtual byte* Generate() = 0; |
| |
| private: |
| #if USE_SIMULATOR && V8_TARGET_ARCH_ARM64 |
| uintptr_t CallSimulator(byte* f, Simulator::CallArgument* args) { |
| Simulator* simulator = Simulator::current(isolate_); |
| return static_cast<uintptr_t>(simulator->CallInt64(f, args)); |
| } |
| |
| template <typename R, typename F> |
| R DoCall(F* f) { |
| Simulator::CallArgument args[] = {Simulator::CallArgument::End()}; |
| return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args)); |
| } |
| template <typename R, typename F, typename P1> |
| R DoCall(F* f, P1 p1) { |
| Simulator::CallArgument args[] = {Simulator::CallArgument(p1), |
| Simulator::CallArgument::End()}; |
| return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args)); |
| } |
| template <typename R, typename F, typename P1, typename P2> |
| R DoCall(F* f, P1 p1, P2 p2) { |
| Simulator::CallArgument args[] = {Simulator::CallArgument(p1), |
| Simulator::CallArgument(p2), |
| Simulator::CallArgument::End()}; |
| return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args)); |
| } |
| template <typename R, typename F, typename P1, typename P2, typename P3> |
| R DoCall(F* f, P1 p1, P2 p2, P3 p3) { |
| Simulator::CallArgument args[] = { |
| Simulator::CallArgument(p1), Simulator::CallArgument(p2), |
| Simulator::CallArgument(p3), Simulator::CallArgument::End()}; |
| return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args)); |
| } |
| template <typename R, typename F, typename P1, typename P2, typename P3, |
| typename P4> |
| R DoCall(F* f, P1 p1, P2 p2, P3 p3, P4 p4) { |
| Simulator::CallArgument args[] = { |
| Simulator::CallArgument(p1), Simulator::CallArgument(p2), |
| Simulator::CallArgument(p3), Simulator::CallArgument(p4), |
| Simulator::CallArgument::End()}; |
| return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args)); |
| } |
| #elif USE_SIMULATOR && (V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS) |
| uintptr_t CallSimulator(byte* f, int32_t p1 = 0, int32_t p2 = 0, |
| int32_t p3 = 0, int32_t p4 = 0) { |
| Simulator* simulator = Simulator::current(isolate_); |
| return static_cast<uintptr_t>(simulator->Call(f, 4, p1, p2, p3, p4)); |
| } |
| template <typename R, typename F> |
| R DoCall(F* f) { |
| return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f))); |
| } |
| template <typename R, typename F, typename P1> |
| R DoCall(F* f, P1 p1) { |
| return ReturnValueTraits<R>::Cast( |
| CallSimulator(FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1))); |
| } |
| template <typename R, typename F, typename P1, typename P2> |
| R DoCall(F* f, P1 p1, P2 p2) { |
| return ReturnValueTraits<R>::Cast( |
| CallSimulator(FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1), |
| ParameterTraits<P2>::Cast(p2))); |
| } |
| template <typename R, typename F, typename P1, typename P2, typename P3> |
| R DoCall(F* f, P1 p1, P2 p2, P3 p3) { |
| return ReturnValueTraits<R>::Cast(CallSimulator( |
| FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1), |
| ParameterTraits<P2>::Cast(p2), ParameterTraits<P3>::Cast(p3))); |
| } |
| template <typename R, typename F, typename P1, typename P2, typename P3, |
| typename P4> |
| R DoCall(F* f, P1 p1, P2 p2, P3 p3, P4 p4) { |
| return ReturnValueTraits<R>::Cast(CallSimulator( |
| FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1), |
| ParameterTraits<P2>::Cast(p2), ParameterTraits<P3>::Cast(p3), |
| ParameterTraits<P4>::Cast(p4))); |
| } |
| #else |
| template <typename R, typename F> |
| R DoCall(F* f) { |
| return f(); |
| } |
| template <typename R, typename F, typename P1> |
| R DoCall(F* f, P1 p1) { |
| return f(p1); |
| } |
| template <typename R, typename F, typename P1, typename P2> |
| R DoCall(F* f, P1 p1, P2 p2) { |
| return f(p1, p2); |
| } |
| template <typename R, typename F, typename P1, typename P2, typename P3> |
| R DoCall(F* f, P1 p1, P2 p2, P3 p3) { |
| return f(p1, p2, p3); |
| } |
| template <typename R, typename F, typename P1, typename P2, typename P3, |
| typename P4> |
| R DoCall(F* f, P1 p1, P2 p2, P3 p3, P4 p4) { |
| return f(p1, p2, p3, p4); |
| } |
| #endif |
| |
| #ifndef DEBUG |
| void VerifyParameters0() {} |
| |
| template <typename P1> |
| void VerifyParameters1() {} |
| |
| template <typename P1, typename P2> |
| void VerifyParameters2() {} |
| |
| template <typename P1, typename P2, typename P3> |
| void VerifyParameters3() {} |
| |
| template <typename P1, typename P2, typename P3, typename P4> |
| void VerifyParameters4() {} |
| #else |
| void VerifyParameters0() { VerifyParameters(0, NULL); } |
| |
| template <typename P1> |
| void VerifyParameters1() { |
| MachineType parameters[] = {ReturnValueTraits<P1>::Representation()}; |
| VerifyParameters(arraysize(parameters), parameters); |
| } |
| |
| template <typename P1, typename P2> |
| void VerifyParameters2() { |
| MachineType parameters[] = {ReturnValueTraits<P1>::Representation(), |
| ReturnValueTraits<P2>::Representation()}; |
| VerifyParameters(arraysize(parameters), parameters); |
| } |
| |
| template <typename P1, typename P2, typename P3> |
| void VerifyParameters3() { |
| MachineType parameters[] = {ReturnValueTraits<P1>::Representation(), |
| ReturnValueTraits<P2>::Representation(), |
| ReturnValueTraits<P3>::Representation()}; |
| VerifyParameters(arraysize(parameters), parameters); |
| } |
| |
| template <typename P1, typename P2, typename P3, typename P4> |
| void VerifyParameters4() { |
| MachineType parameters[] = {ReturnValueTraits<P1>::Representation(), |
| ReturnValueTraits<P2>::Representation(), |
| ReturnValueTraits<P3>::Representation(), |
| ReturnValueTraits<P4>::Representation()}; |
| VerifyParameters(arraysize(parameters), parameters); |
| } |
| #endif |
| |
| // TODO(dcarney): replace Call() in CallHelper2 with these. |
| template <typename R> |
| R Call0() { |
| typedef R V8_CDECL FType(); |
| VerifyParameters0(); |
| return DoCall<R>(FUNCTION_CAST<FType*>(Generate())); |
| } |
| |
| template <typename R, typename P1> |
| R Call1(P1 p1) { |
| typedef R V8_CDECL FType(P1); |
| VerifyParameters1<P1>(); |
| return DoCall<R>(FUNCTION_CAST<FType*>(Generate()), p1); |
| } |
| |
| template <typename R, typename P1, typename P2> |
| R Call2(P1 p1, P2 p2) { |
| typedef R V8_CDECL FType(P1, P2); |
| VerifyParameters2<P1, P2>(); |
| return DoCall<R>(FUNCTION_CAST<FType*>(Generate()), p1, p2); |
| } |
| |
| template <typename R, typename P1, typename P2, typename P3> |
| R Call3(P1 p1, P2 p2, P3 p3) { |
| typedef R V8_CDECL FType(P1, P2, P3); |
| VerifyParameters3<P1, P2, P3>(); |
| return DoCall<R>(FUNCTION_CAST<FType*>(Generate()), p1, p2, p3); |
| } |
| |
| template <typename R, typename P1, typename P2, typename P3, typename P4> |
| R Call4(P1 p1, P2 p2, P3 p3, P4 p4) { |
| typedef R V8_CDECL FType(P1, P2, P3, P4); |
| VerifyParameters4<P1, P2, P3, P4>(); |
| return DoCall<R>(FUNCTION_CAST<FType*>(Generate()), p1, p2, p3, p4); |
| } |
| |
| template <typename R, typename C> |
| friend class CallHelper2; |
| Isolate* isolate_; |
| }; |
| |
| |
| // TODO(dcarney): replace CallHelper with CallHelper2 and rename. |
| template <typename R, typename C> |
| class CallHelper2 { |
| public: |
| R Call() { return helper()->template Call0<R>(); } |
| |
| template <typename P1> |
| R Call(P1 p1) { |
| return helper()->template Call1<R>(p1); |
| } |
| |
| template <typename P1, typename P2> |
| R Call(P1 p1, P2 p2) { |
| return helper()->template Call2<R>(p1, p2); |
| } |
| |
| template <typename P1, typename P2, typename P3> |
| R Call(P1 p1, P2 p2, P3 p3) { |
| return helper()->template Call3<R>(p1, p2, p3); |
| } |
| |
| template <typename P1, typename P2, typename P3, typename P4> |
| R Call(P1 p1, P2 p2, P3 p3, P4 p4) { |
| return helper()->template Call4<R>(p1, p2, p3, p4); |
| } |
| |
| private: |
| CallHelper* helper() { return static_cast<C*>(this); } |
| }; |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_CCTEST_COMPILER_CALL_TESTER_H_ |