// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #ifndef RUNTIME_VM_CONSTANTS_IA32_H_ #define RUNTIME_VM_CONSTANTS_IA32_H_ #ifndef RUNTIME_VM_CONSTANTS_H_ #error Do not include constants_ia32.h directly; use constants.h instead. #endif #include "platform/assert.h" #include "platform/globals.h" #include "platform/utils.h" #include "vm/constants_base.h" namespace dart { #define R(reg) (1 << (reg)) enum Register { EAX = 0, ECX = 1, EDX = 2, EBX = 3, ESP = 4, // SP EBP = 5, // FP ESI = 6, // THR EDI = 7, kNumberOfCpuRegisters = 8, kNoRegister = -1, // Signals an illegal register. }; // Low and high bytes registers of the first four general purpose registers. // The other four general purpose registers do not have byte registers. enum ByteRegister { AL = 0, CL = 1, DL = 2, BL = 3, AH = 4, CH = 5, DH = 6, BH = 7, kNumberOfByteRegisters = 8, kNoByteRegister = -1 // Signals an illegal register. }; inline ByteRegister ByteRegisterOf(Register reg) { // This only works for EAX, ECX, EDX, EBX. // Remaining Register values map to high byte of the above registers. RELEASE_ASSERT(reg == Register::EAX || reg == Register::ECX || reg == Register::EDX || reg == Register::EBX); return static_cast(reg); } enum XmmRegister { XMM0 = 0, XMM1 = 1, XMM2 = 2, XMM3 = 3, XMM4 = 4, XMM5 = 5, XMM6 = 6, XMM7 = 7, kNumberOfXmmRegisters = 8, kNoXmmRegister = -1 // Signals an illegal register. }; // Architecture independent aliases. typedef XmmRegister FpuRegister; const FpuRegister FpuTMP = XMM7; const int kFpuRegisterSize = 16; typedef simd128_value_t fpu_register_t; const int kNumberOfFpuRegisters = kNumberOfXmmRegisters; const FpuRegister kNoFpuRegister = kNoXmmRegister; extern const char* const cpu_reg_names[kNumberOfCpuRegisters]; extern const char* const cpu_reg_abi_names[kNumberOfCpuRegisters]; extern const char* const cpu_reg_byte_names[kNumberOfByteRegisters]; extern const char* const fpu_reg_names[kNumberOfXmmRegisters]; // Register aliases. const Register TMP = kNoRegister; // No scratch register used by assembler. const Register TMP2 = kNoRegister; // No second assembler scratch register. const Register CODE_REG = EDI; // Set when calling Dart functions in JIT mode, used by LazyCompileStub. const Register FUNCTION_REG = EAX; const Register PP = kNoRegister; // No object pool pointer. const Register SPREG = ESP; // Stack pointer register. const Register FPREG = EBP; // Frame pointer register. const Register IC_DATA_REG = ECX; // ICData/MegamorphicCache register. const Register ARGS_DESC_REG = EDX; // Arguments descriptor register. const Register THR = ESI; // Caches current thread in generated code. const Register CALLEE_SAVED_TEMP = EBX; const Register CALLEE_SAVED_TEMP2 = EDI; // ABI for catch-clause entry point. const Register kExceptionObjectReg = EAX; const Register kStackTraceObjectReg = EDX; // ABI for write barrier stub. const Register kWriteBarrierObjectReg = EDX; const Register kWriteBarrierValueReg = EBX; const Register kWriteBarrierSlotReg = EDI; // Common ABI for shared slow path stubs. struct SharedSlowPathStubABI { static constexpr Register kResultReg = EAX; }; // ABI for instantiation stubs. struct InstantiationABI { static constexpr Register kUninstantiatedTypeArgumentsReg = EBX; static constexpr Register kInstantiatorTypeArgumentsReg = EDX; static constexpr Register kFunctionTypeArgumentsReg = ECX; static constexpr Register kResultTypeArgumentsReg = EAX; static constexpr Register kResultTypeReg = EAX; static constexpr Register kScratchReg = EDI; // On ia32 we don't use CODE_REG. }; // Registers in addition to those listed in InstantiationABI used inside the // implementation of the InstantiateTypeArguments stubs. struct InstantiateTAVInternalRegs { // On IA32, we don't do hash cache checks in the stub. We only define // kSavedRegisters to avoid needing to #ifdef uses of it. static constexpr intptr_t kSavedRegisters = 0; }; // Calling convention when calling SubtypeTestCacheStub. // Although ia32 uses a stack-based calling convention, we keep the same // 'TypeTestABI' name for symmetry with other architectures with a proper ABI. // Note that ia32 has no support for type testing stubs. struct TypeTestABI { static constexpr Register kInstanceReg = EAX; static constexpr Register kDstTypeReg = EBX; static constexpr Register kInstantiatorTypeArgumentsReg = EDX; static constexpr Register kFunctionTypeArgumentsReg = ECX; static constexpr Register kSubtypeTestCacheReg = EDI; // On ia32 we don't use CODE_REG. // For call to InstanceOfStub. static constexpr Register kInstanceOfResultReg = kInstanceReg; // For call to SubtypeNTestCacheStub. static constexpr Register kSubtypeTestCacheResultReg = TypeTestABI::kSubtypeTestCacheReg; }; // Calling convention when calling kSubtypeCheckRuntimeEntry, to match other // architectures. We don't generate a call to the AssertSubtypeStub because we // need CODE_REG to store a fifth argument. struct AssertSubtypeABI { static constexpr Register kSubTypeReg = EAX; static constexpr Register kSuperTypeReg = EBX; static constexpr Register kInstantiatorTypeArgumentsReg = EDX; static constexpr Register kFunctionTypeArgumentsReg = ECX; static constexpr Register kDstNameReg = EDI; /// On ia32 we don't use CODE_REG. // No result register, as AssertSubtype is only run for side effect // (throws if the subtype check fails). }; // For calling the ia32-specific AssertAssignableStub struct AssertAssignableStubABI { static constexpr Register kDstNameReg = EBX; static constexpr Register kSubtypeTestReg = ECX; static constexpr intptr_t kInstanceSlotFromFp = 2 + 3; static constexpr intptr_t kDstTypeSlotFromFp = 2 + 2; static constexpr intptr_t kInstantiatorTAVSlotFromFp = 2 + 1; static constexpr intptr_t kFunctionTAVSlotFromFp = 2 + 0; }; // ABI for InitStaticFieldStub. struct InitStaticFieldABI { static constexpr Register kFieldReg = EDX; static constexpr Register kResultReg = EAX; }; // Registers used inside the implementation of InitLateStaticFieldStub. struct InitLateStaticFieldInternalRegs { static constexpr Register kAddressReg = ECX; static constexpr Register kScratchReg = EDI; }; // ABI for InitInstanceFieldStub. struct InitInstanceFieldABI { static constexpr Register kInstanceReg = EBX; static constexpr Register kFieldReg = EDX; static constexpr Register kResultReg = EAX; }; // Registers used inside the implementation of InitLateInstanceFieldStub. struct InitLateInstanceFieldInternalRegs { static constexpr Register kAddressReg = ECX; static constexpr Register kScratchReg = EDI; }; // ABI for LateInitializationError stubs. struct LateInitializationErrorABI { static constexpr Register kFieldReg = EDI; }; // ABI for FieldAccessError stubs. struct FieldAccessErrorABI { static constexpr Register kFieldReg = EDI; }; // ABI for ThrowStub. struct ThrowABI { static constexpr Register kExceptionReg = EAX; }; // ABI for ReThrowStub. struct ReThrowABI { static constexpr Register kExceptionReg = EAX; static constexpr Register kStackTraceReg = EBX; }; // ABI for RangeErrorStub. struct RangeErrorABI { static constexpr Register kLengthReg = EAX; static constexpr Register kIndexReg = EBX; }; // ABI for AllocateObjectStub. struct AllocateObjectABI { static constexpr Register kResultReg = EAX; static constexpr Register kTypeArgumentsReg = EDX; static constexpr Register kTagsReg = kNoRegister; // Not used. }; // ABI for Allocate{Mint,Double,Float32x4,Float64x2}Stub. struct AllocateBoxABI { static constexpr Register kResultReg = AllocateObjectABI::kResultReg; static constexpr Register kTempReg = EBX; }; // ABI for AllocateClosureStub. struct AllocateClosureABI { static constexpr Register kResultReg = AllocateObjectABI::kResultReg; static constexpr Register kFunctionReg = EBX; static constexpr Register kLengthAndFlagsReg = ECX; static constexpr Register kContextReg = EDI; static constexpr Register kScratchReg = EDX; }; // ABI for AllocateArrayStub. struct AllocateArrayABI { static constexpr Register kResultReg = AllocateObjectABI::kResultReg; static constexpr Register kLengthReg = EDX; static constexpr Register kTypeArgumentsReg = ECX; }; // ABI for AllocateRecordStub. struct AllocateRecordABI { static constexpr Register kResultReg = AllocateObjectABI::kResultReg; static constexpr Register kShapeReg = EDX; static constexpr Register kTemp1Reg = EBX; static constexpr Register kTemp2Reg = EDI; }; // ABI for AllocateSmallRecordStub (AllocateRecord2, AllocateRecord2Named, // AllocateRecord3, AllocateRecord3Named). struct AllocateSmallRecordABI { static constexpr Register kResultReg = AllocateObjectABI::kResultReg; static constexpr Register kShapeReg = EBX; static constexpr Register kValue0Reg = ECX; static constexpr Register kValue1Reg = EDX; static constexpr Register kValue2Reg = kNoRegister; static constexpr Register kTempReg = EDI; }; // ABI for AllocateTypedDataArrayStub. struct AllocateTypedDataArrayABI { static constexpr Register kResultReg = AllocateObjectABI::kResultReg; static constexpr Register kLengthReg = kResultReg; }; // ABI for BoxDoubleStub. struct BoxDoubleStubABI { static constexpr FpuRegister kValueReg = XMM0; static constexpr Register kTempReg = EBX; static constexpr Register kResultReg = EAX; }; // ABI for DoubleToIntegerStub. struct DoubleToIntegerStubABI { static constexpr FpuRegister kInputReg = XMM0; static constexpr Register kRecognizedKindReg = EAX; static constexpr Register kResultReg = EAX; }; // ABI for CheckedStoreIntoSharedStub. struct CheckedStoreIntoSharedStubABI { static constexpr Register kFieldReg = EBX; static constexpr Register kValueReg = EDX; static constexpr Register kResultReg = EAX; }; // ABI for EnsureDeeplyImmutableStub. struct EnsureDeeplyImmutableStubABI { static constexpr Register kValueReg = EAX; static constexpr Register kTempReg = ECX; }; // ABI for SuspendStub (AwaitStub, AwaitWithTypeCheckStub, YieldAsyncStarStub, // SuspendSyncStarAtStartStub, SuspendSyncStarAtYieldStub). struct SuspendStubABI { static constexpr Register kArgumentReg = EAX; static constexpr Register kTypeArgsReg = EDX; // Can be the same as kTempReg static constexpr Register kTempReg = EDX; static constexpr Register kFrameSizeReg = ECX; static constexpr Register kSuspendStateReg = EBX; static constexpr Register kFunctionDataReg = EDI; // Can reuse THR. static constexpr Register kSrcFrameReg = ESI; // Can reuse kFunctionDataReg. static constexpr Register kDstFrameReg = EDI; // Number of bytes to skip after // suspend stub return address in order to resume. // IA32: mov esp, ebp; pop ebp; ret static constexpr intptr_t kResumePcDistance = 4; }; // ABI for InitSuspendableFunctionStub (InitAsyncStub, InitAsyncStarStub, // InitSyncStarStub). struct InitSuspendableFunctionStubABI { static constexpr Register kTypeArgsReg = EAX; }; // ABI for ResumeStub struct ResumeStubABI { static constexpr Register kSuspendStateReg = EBX; static constexpr Register kTempReg = EDX; // Registers for the frame copying (the 1st part). static constexpr Register kFrameSizeReg = ECX; // Can reuse THR. static constexpr Register kSrcFrameReg = ESI; // Can reuse CODE_REG. static constexpr Register kDstFrameReg = EDI; // Registers for control transfer. // (the 2nd part, can reuse registers from the 1st part) static constexpr Register kResumePcReg = ECX; // Can also reuse kSuspendStateReg but should not conflict with CODE_REG. static constexpr Register kExceptionReg = EAX; static constexpr Register kStackTraceReg = EBX; }; // ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub, // ReturnAsyncStarStub). struct ReturnStubABI { static constexpr Register kSuspendStateReg = EBX; }; // ABI for AsyncExceptionHandlerStub. struct AsyncExceptionHandlerStubABI { static constexpr Register kSuspendStateReg = EBX; }; // ABI for CloneSuspendStateStub. struct CloneSuspendStateStubABI { static constexpr Register kSourceReg = EAX; static constexpr Register kDestinationReg = EBX; static constexpr Register kTempReg = EDX; static constexpr Register kFrameSizeReg = ECX; // Can reuse THR. static constexpr Register kSrcFrameReg = ESI; static constexpr Register kDstFrameReg = EDI; }; // ABI for FfiAsyncCallbackSendStub. struct FfiAsyncCallbackSendStubABI { static constexpr Register kArgsReg = EAX; }; // ABI for DispatchTableNullErrorStub and consequently for all dispatch // table calls (though normal functions will not expect or use this // register). This ABI is added to distinguish memory corruption errors from // null errors. // Note: dispatch table calls are never actually generated on IA32, this // declaration is only added for completeness. struct DispatchTableNullErrorABI { static constexpr Register kClassIdReg = EAX; }; typedef uint32_t RegList; const RegList kAllCpuRegistersList = 0xFF; const RegList kAllFpuRegistersList = (1 << kNumberOfFpuRegisters) - 1; const intptr_t kReservedCpuRegisters = (1 << SPREG) | (1 << FPREG) | (1 << THR); constexpr intptr_t kNumberOfReservedCpuRegisters = Utils::CountOneBits32(kReservedCpuRegisters); // CPU registers available to Dart allocator. const RegList kDartAvailableCpuRegs = kAllCpuRegistersList & ~kReservedCpuRegisters; constexpr int kNumberOfDartAvailableCpuRegs = kNumberOfCpuRegisters - kNumberOfReservedCpuRegisters; // No reason to prefer certain registers on IA32. constexpr int kRegisterAllocationBias = 0; constexpr int kStoreBufferWrapperSize = 11; const RegList kAbiPreservedCpuRegs = (1 << EDI) | (1 << ESI) | (1 << EBX); const RegList kAbiVolatileFpuRegs = kAllFpuRegistersList; // Registers available to Dart that are not preserved by runtime calls. const RegList kDartVolatileCpuRegs = kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs; const RegList kDartVolatileFpuRegs = kAbiVolatileFpuRegs & ~(1 << FpuTMP); #undef R enum ScaleFactor { TIMES_1 = 0, TIMES_2 = 1, TIMES_4 = 2, TIMES_8 = 3, TIMES_16 = 4, // We can't include vm/compiler/runtime_api.h, so just be explicit instead // of using (dart::)kWordSizeLog2. #if defined(TARGET_ARCH_IS_32_BIT) // Used for Smi-boxed indices. TIMES_HALF_WORD_SIZE = kInt32SizeLog2 - 1, // Used for unboxed indices. TIMES_WORD_SIZE = kInt32SizeLog2, #else #error "Unexpected word size" #endif #if !defined(DART_COMPRESSED_POINTERS) TIMES_COMPRESSED_WORD_SIZE = TIMES_WORD_SIZE, #else #error Cannot compress IA32 #endif // Used for Smi-boxed indices. TIMES_COMPRESSED_HALF_WORD_SIZE = TIMES_COMPRESSED_WORD_SIZE - 1, }; class Instr { public: static constexpr uint8_t kHltInstruction = 0xF4; // We prefer not to use the int3 instruction since it conflicts with gdb. static constexpr uint8_t kBreakPointInstruction = kHltInstruction; static constexpr int kBreakPointInstructionSize = 1; bool IsBreakPoint() { ASSERT(kBreakPointInstructionSize == 1); return (*reinterpret_cast(this)) == kBreakPointInstruction; } // Instructions are read out of a code stream. The only way to get a // reference to an instruction is to convert a pointer. There is no way // to allocate or create instances of class Instr. // Use the At(pc) function to create references to Instr. static Instr* At(uword pc) { return reinterpret_cast(pc); } private: DISALLOW_ALLOCATION(); // We need to prevent the creation of instances of class Instr. DISALLOW_IMPLICIT_CONSTRUCTORS(Instr); }; // The largest multibyte nop we will emit. This could go up to 15 if it // becomes important to us. const int MAX_NOP_SIZE = 8; class CallingConventions { public: static const Register ArgumentRegisters[]; static constexpr intptr_t kArgumentRegisters = 0; static constexpr intptr_t kFpuArgumentRegisters = 0; static constexpr intptr_t kNumArgRegs = 0; static constexpr Register kPointerToReturnStructRegisterCall = kNoRegister; static const XmmRegister FpuArgumentRegisters[]; static constexpr intptr_t kXmmArgumentRegisters = 0; static constexpr intptr_t kNumFpuArgRegs = 0; static constexpr intptr_t kCalleeSaveCpuRegisters = kAbiPreservedCpuRegs; static constexpr bool kArgumentIntRegXorFpuReg = false; static constexpr Register kReturnReg = EAX; static constexpr Register kSecondReturnReg = EDX; static constexpr Register kPointerToReturnStructRegisterReturn = kReturnReg; // Whether the callee uses `ret 4` instead of `ret` to return with struct // return values. // See: https://c9x.me/x86/html/file_module_x86_id_280.html #if defined(_WIN32) static constexpr bool kUsesRet4 = false; #else static constexpr bool kUsesRet4 = true; #endif // Floating point values are returned on the "FPU stack" (in "ST" registers). // However, we use XMM0 in our compiler pipeline as the location. // The move from and to ST is done in FfiCallInstr::EmitNativeCode and // NativeReturnInstr::EmitNativeCode. static constexpr XmmRegister kReturnFpuReg = XMM0; static constexpr Register kFfiAnyNonAbiRegister = EBX; static constexpr Register kFirstNonArgumentRegister = EAX; static constexpr Register kSecondNonArgumentRegister = ECX; static constexpr Register kStackPointerRegister = SPREG; // Whether larger than wordsize arguments are aligned to even registers. static constexpr AlignmentStrategy kArgumentRegisterAlignment = kAlignedToWordSize; static constexpr AlignmentStrategy kArgumentRegisterAlignmentVarArgs = kArgumentRegisterAlignment; // How stack arguments are aligned. static constexpr AlignmentStrategy kArgumentStackAlignment = kAlignedToWordSize; static constexpr AlignmentStrategy kArgumentStackAlignmentVarArgs = kArgumentStackAlignment; // How fields in compounds are aligned. #if defined(DART_TARGET_OS_WINDOWS) static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize; #else static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSizeBut8AlignedTo4; #endif // Whether 1 or 2 byte-sized arguments or return values are passed extended // to 4 bytes. static constexpr ExtensionStrategy kReturnRegisterExtension = kNotExtended; static constexpr ExtensionStrategy kArgumentRegisterExtension = kNotExtended; static constexpr ExtensionStrategy kArgumentStackExtension = kExtendedTo4; }; // Register based calling convention used for Dart functions. // // See |compiler::ComputeCallingConvention| for more details. struct DartCallingConvention { static constexpr Register kCpuRegistersForArgs[] = {kNoRegister}; static constexpr FpuRegister kFpuRegistersForArgs[] = {kNoFpuRegister}; }; const uword kBreakInstructionFiller = 0xCCCCCCCC; // Prioritize code size over performance. const intptr_t kPreferredLoopAlignment = 1; } // namespace dart #endif // RUNTIME_VM_CONSTANTS_IA32_H_