diff --git a/src/compiler/evm_frontend/evm_mir_compiler.cpp b/src/compiler/evm_frontend/evm_mir_compiler.cpp index 8f485fe16..70adb5761 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.cpp +++ b/src/compiler/evm_frontend/evm_mir_compiler.cpp @@ -109,10 +109,116 @@ void EVMMirBuilder::loadEVMInstanceAttr() { false, OP_add, &Ctx.I64Type, StackBaseAddr, StackSize); createInstruction(true, &(Ctx.VoidType), StackTopAddr, StackTopVar->getVarIdx()); + // Initialize jump target variable + JumpTargetVar = CurFunc->createVariable(&Ctx.I64Type); ExceptionReturnBB = CurFunc->createExceptionReturnBB(); } +MBasicBlock *EVMMirBuilder::getOrCreateIndirectJumpBB() { + if (IndirectJumpBB) { + return IndirectJumpBB; + } + + MBasicBlock *FromBB = CurBB; + IndirectJumpBB = CurFunc->createBasicBlock(); + setInsertBlock(IndirectJumpBB); +#ifdef ZEN_ENABLE_LINUX_PERF + CurBB->setSourceOffset(CurPC); + CurBB->setSourceName("SWITCH" + std::to_string(CurInstrIdx)); + CurInstrIdx++; +#endif // ZEN_ENABLE_LINUX_PERF + + MBasicBlock *FailureBB = + getOrCreateExceptionSetBB(ErrorCode::EVMBadJumpDestination); + MInstruction *JumpTarget = loadVariable(JumpTargetVar); + MType *UInt64Type = + EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); + + // If hash table is used, create mir to calculate hash index of JumpTarget + // PC and create switch instruction with hash index + if (!JumpHashTable.empty()) { + // Initialize hash cases + uint64_t MinHash = JumpHashTable.begin()->first; + uint64_t MaxHash = JumpHashTable.rbegin()->first; + CompileVector> HashCases( + MaxHash - MinHash + 1, Ctx.MemPool); + + // Calculate hash of JumpTarget + MInstruction *MulConst = + createIntConstInstruction(UInt64Type, HashMultiplier); + MInstruction *MulResult = createInstruction( + false, OP_mul, UInt64Type, JumpTarget, MulConst); + MInstruction *AndResult = createInstruction( + false, OP_and, UInt64Type, MulResult, + createIntConstInstruction(UInt64Type, HashMask)); + MInstruction *HashDest = protectUnsafeValue(AndResult, UInt64Type); + + // Create cases for each hash entry + for (uint64_t HashEntry = MinHash; HashEntry <= MaxHash; HashEntry++) { + uint64_t HIndex = HashEntry - MinHash; + HashCases[HIndex].first = + createIntConstInstruction(UInt64Type, HashEntry); + if (JumpHashTable.count(HashEntry) == 0) { + // FailureBB for empty hash index + HashCases[HIndex].second = FailureBB; + addUniqueSuccessor(FailureBB); + continue; + } + if (JumpHashTable[HashEntry].size() == 1) { + // JumpDest BB for no-conflict hash index + HashCases[HIndex].second = JumpHashTable[HashEntry][0]; + addSuccessor(JumpHashTable[HashEntry][0]); + } else { + // Create switch for conflict hash items + MBasicBlock *OutsideBB = CurBB; + MBasicBlock *SubCaseBB = createBasicBlock(); + SubCaseBB->setJumpDestBB(true); + // Enter subcase BB + setInsertBlock(SubCaseBB); + auto &SubPCVec = JumpHashReverse[HashEntry]; + auto &SubDestBBVec = JumpHashTable[HashEntry]; + CompileVector> SubCases( + SubDestBBVec.size(), Ctx.MemPool); + for (size_t I = 0; I < SubDestBBVec.size(); I++) { + SubCases[I].first = + createIntConstInstruction(UInt64Type, SubPCVec[I]); + SubCases[I].second = SubDestBBVec[I]; + addSuccessor(SubDestBBVec[I]); + } + createInstruction(true, Ctx, JumpTarget, FailureBB, + SubCases); + addUniqueSuccessor(FailureBB); + // Back to outside BB + setInsertBlock(OutsideBB); + HashCases[HIndex].second = SubCaseBB; + addSuccessor(SubCaseBB); + } + } + createInstruction(true, Ctx, HashDest, FailureBB, + HashCases); + addUniqueSuccessor(FailureBB); + setInsertBlock(FromBB); + return IndirectJumpBB; + } + + CompileVector> Cases( + JumpDestTable.size(), Ctx.MemPool); + + uint64_t Index = 0; + for (const auto &[DestPC, DestBB] : JumpDestTable) { + Cases[Index].first = createIntConstInstruction(UInt64Type, DestPC); + Cases[Index].second = DestBB; + addSuccessor(DestBB); + Index++; + } + + createInstruction(true, Ctx, JumpTarget, FailureBB, Cases); + addUniqueSuccessor(FailureBB); + setInsertBlock(FromBB); + return IndirectJumpBB; +} + void EVMMirBuilder::initEVM(CompilerContext *Context) { // Create entry basic block MBasicBlock *EntryBB = createBasicBlock(); @@ -781,94 +887,11 @@ void EVMMirBuilder::implementIndirectJump(MInstruction *JumpTarget, } HasIndirectJump = true; -#ifdef ZEN_ENABLE_LINUX_PERF - CurBB->setSourceOffset(CurPC); - CurBB->setSourceName("SWITCH" + std::to_string(CurInstrIdx)); - CurInstrIdx++; -#endif // ZEN_ENABLE_LINUX_PERF - - MType *UInt64Type = - EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); - - // If hash table is used, create mir to calculate hash index of JumpTarget - // PC and create switch instruction with hash index - if (!JumpHashTable.empty()) { - // Initialize hash cases - uint64_t MinHash = JumpHashTable.begin()->first; - uint64_t MaxHash = JumpHashTable.rbegin()->first; - CompileVector> HashCases( - MaxHash - MinHash + 1, Ctx.MemPool); - - // Calculate hash of JumpTarget - MInstruction *MulConst = - createIntConstInstruction(UInt64Type, HashMultiplier); - MInstruction *MulResult = createInstruction( - false, OP_mul, UInt64Type, JumpTarget, MulConst); - MInstruction *AndResult = createInstruction( - false, OP_and, UInt64Type, MulResult, - createIntConstInstruction(UInt64Type, HashMask)); - MInstruction *HashDest = protectUnsafeValue(AndResult, UInt64Type); - - // Create cases for each hash entry - for (uint64_t HashEntry = MinHash; HashEntry <= MaxHash; HashEntry++) { - uint64_t HIndex = HashEntry - MinHash; - HashCases[HIndex].first = - createIntConstInstruction(UInt64Type, HashEntry); - if (JumpHashTable.count(HashEntry) == 0) { - // FailureBB for empty hash index - HashCases[HIndex].second = FailureBB; - addUniqueSuccessor(FailureBB); - continue; - } - if (JumpHashTable[HashEntry].size() == 1) { - // JumpDest BB for no-conflict hash index - HashCases[HIndex].second = JumpHashTable[HashEntry][0]; - addSuccessor(JumpHashTable[HashEntry][0]); - } else { - // Create switch for conflict hash items - MBasicBlock *OutsideBB = CurBB; - MBasicBlock *SubCaseBB = createBasicBlock(); - SubCaseBB->setJumpDestBB(true); - // Enter subcase BB - setInsertBlock(SubCaseBB); - auto &SubPCVec = JumpHashReverse[HashEntry]; - auto &SubDestBBVec = JumpHashTable[HashEntry]; - CompileVector> SubCases( - SubDestBBVec.size(), Ctx.MemPool); - for (size_t I = 0; I < SubDestBBVec.size(); I++) { - SubCases[I].first = - createIntConstInstruction(UInt64Type, SubPCVec[I]); - SubCases[I].second = SubDestBBVec[I]; - addSuccessor(SubDestBBVec[I]); - } - createInstruction(true, Ctx, JumpTarget, FailureBB, - SubCases); - addUniqueSuccessor(FailureBB); - // Back to outside BB - setInsertBlock(OutsideBB); - HashCases[HIndex].second = SubCaseBB; - addSuccessor(SubCaseBB); - } - } - createInstruction(true, Ctx, HashDest, FailureBB, - HashCases); - addUniqueSuccessor(FailureBB); - return; - } - - CompileVector> Cases( - JumpDestTable.size(), Ctx.MemPool); - - uint64_t Index = 0; - for (const auto &[DestPC, DestBB] : JumpDestTable) { - Cases[Index].first = createIntConstInstruction(UInt64Type, DestPC); - Cases[Index].second = DestBB; - addSuccessor(DestBB); - Index++; - } - - createInstruction(true, Ctx, JumpTarget, FailureBB, Cases); - addUniqueSuccessor(FailureBB); + MBasicBlock *TargetBB = getOrCreateIndirectJumpBB(); + createInstruction(true, &(Ctx.VoidType), JumpTarget, + JumpTargetVar->getVarIdx()); + createInstruction(true, Ctx, TargetBB); + addUniqueSuccessor(TargetBB); } // ==================== Stack Instruction Handlers ==================== diff --git a/src/compiler/evm_frontend/evm_mir_compiler.h b/src/compiler/evm_frontend/evm_mir_compiler.h index 2d859c040..5d739e04e 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.h +++ b/src/compiler/evm_frontend/evm_mir_compiler.h @@ -594,6 +594,8 @@ class EVMMirBuilder final { template U256Inst convertOperandToUNInstruction(const Operand &Param); + MBasicBlock *getOrCreateIndirectJumpBB(); + CompilerContext &Ctx; MFunction *CurFunc = nullptr; MBasicBlock *CurBB = nullptr; @@ -617,6 +619,8 @@ class EVMMirBuilder final { std::map> JumpHashTable; std::map> JumpHashReverse; uint64_t HashMask = 0; + Variable *JumpTargetVar = nullptr; + MBasicBlock *IndirectJumpBB = nullptr; // Stack check block for stack overflow/underflow checking MBasicBlock *StackCheckBB = nullptr;