diff --git a/codegenerator.cpp b/codegenerator.cpp index ea8e2112f..39000d127 100644 --- a/codegenerator.cpp +++ b/codegenerator.cpp @@ -637,7 +637,8 @@ void CodeGenerator::translate(uint64_t VirtualAddress, // Instantiate helpers VariableManager Variables(*TheModule, - *HelpersModule); + *HelpersModule, + TargetArchitecture); auto *PCReg = Variables.getByEnvOffset(ptc.pc, "pc").first; JumpTargetManager JumpTargets(MainFunction, diff --git a/instructiontranslator.cpp b/instructiontranslator.cpp index cda2270c1..08ab2f4de 100644 --- a/instructiontranslator.cpp +++ b/instructiontranslator.cpp @@ -889,13 +889,66 @@ InstructionTranslator::translateOpcode(PTCOpcode Opcode, return std::errc::invalid_argument; } - Value *Target = Variables.getByEnvOffset(ConstArguments[0]); + bool Signed; + switch (Opcode) { + case PTC_INSTRUCTION_op_ld_i32: + case PTC_INSTRUCTION_op_ld_i64: - auto *LoadEnvField = Builder.CreateLoad(Target); - Variables.setAliasScope(LoadEnvField); - Value *Fitted = Builder.CreateZExtOrTrunc(LoadEnvField, RegisterType); + case PTC_INSTRUCTION_op_ld8u_i32: + case PTC_INSTRUCTION_op_ld16u_i32: + case PTC_INSTRUCTION_op_ld8u_i64: + case PTC_INSTRUCTION_op_ld16u_i64: + case PTC_INSTRUCTION_op_ld32u_i64: + Signed = false; + break; + case PTC_INSTRUCTION_op_ld8s_i32: + case PTC_INSTRUCTION_op_ld16s_i32: + case PTC_INSTRUCTION_op_ld8s_i64: + case PTC_INSTRUCTION_op_ld16s_i64: + case PTC_INSTRUCTION_op_ld32s_i64: + Signed = true; + break; + default: + assert(false); + } + + unsigned LoadSize; + switch (Opcode) { + case PTC_INSTRUCTION_op_ld8u_i32: + case PTC_INSTRUCTION_op_ld8s_i32: + case PTC_INSTRUCTION_op_ld8u_i64: + case PTC_INSTRUCTION_op_ld8s_i64: + LoadSize = 1; + break; + case PTC_INSTRUCTION_op_ld16u_i32: + case PTC_INSTRUCTION_op_ld16s_i32: + case PTC_INSTRUCTION_op_ld16u_i64: + case PTC_INSTRUCTION_op_ld16s_i64: + LoadSize = 2; + break; + case PTC_INSTRUCTION_op_ld_i32: + case PTC_INSTRUCTION_op_ld32u_i64: + case PTC_INSTRUCTION_op_ld32s_i64: + LoadSize = 4; + break; + case PTC_INSTRUCTION_op_ld_i64: + LoadSize = 8; + break; + default: + assert(false); + } + + Value *Result = Variables.loadFromEnvOffset(Builder, + LoadSize, + ConstArguments[0]); + assert(Result != nullptr); + + // Zero/sign extend in the target dimension + if (Signed) + return v { Builder.CreateSExt(Result, RegisterType) }; + else + return v { Builder.CreateZExt(Result, RegisterType) }; - return v { Fitted }; } case PTC_INSTRUCTION_op_st8_i32: case PTC_INSTRUCTION_op_st16_i32: @@ -905,17 +958,39 @@ InstructionTranslator::translateOpcode(PTCOpcode Opcode, case PTC_INSTRUCTION_op_st32_i64: case PTC_INSTRUCTION_op_st_i64: { + unsigned StoreSize; + switch (Opcode) { + case PTC_INSTRUCTION_op_st8_i32: + case PTC_INSTRUCTION_op_st8_i64: + StoreSize = 1; + break; + case PTC_INSTRUCTION_op_st16_i32: + case PTC_INSTRUCTION_op_st16_i64: + StoreSize = 2; + break; + case PTC_INSTRUCTION_op_st_i32: + case PTC_INSTRUCTION_op_st32_i64: + StoreSize = 4; + break; + case PTC_INSTRUCTION_op_st_i64: + StoreSize = 8; + break; + default: + assert(false); + } + Value *Base = dyn_cast(InArguments[1])->getPointerOperand(); if (Base == nullptr || !Variables.isEnv(Base)) { // TODO: emit warning return std::errc::invalid_argument; } - Value *Target = Variables.getByEnvOffset(ConstArguments[0]); - Type *TargetPointer = Target->getType()->getPointerElementType(); - Value *ToStore = Builder.CreateZExt(InArguments[0], TargetPointer); - auto *Store = Builder.CreateStore(ToStore, Target); - Variables.setAliasScope(Store); + bool Result = Variables.storeToEnvOffset(Builder, + StoreSize, + ConstArguments[0], + InArguments[0]); + assert(Result); + return v { }; } case PTC_INSTRUCTION_op_add_i32: @@ -1267,28 +1342,28 @@ InstructionTranslator::translateOpcode(PTCOpcode Opcode, case PTC_INSTRUCTION_op_add2_i64: case PTC_INSTRUCTION_op_sub2_i64: { - Value *FirstOperandLow = nullptr; - Value *FirstOperandHigh = nullptr; - Value *SecondOperandLow = nullptr; - Value *SecondOperandHigh = nullptr; + Value *FirstOpLow = nullptr; + Value *FirstOpHigh = nullptr; + Value *SecondOpLow = nullptr; + Value *SecondOpHigh = nullptr; IntegerType *DestinationType = Builder.getIntNTy(RegisterSize * 2); - FirstOperandLow = Builder.CreateZExt(InArguments[0], DestinationType); - FirstOperandHigh = Builder.CreateZExt(InArguments[1], DestinationType); - SecondOperandLow = Builder.CreateZExt(InArguments[2], DestinationType); - SecondOperandHigh = Builder.CreateZExt(InArguments[3], DestinationType); + FirstOpLow = Builder.CreateZExt(InArguments[0], DestinationType); + FirstOpHigh = Builder.CreateZExt(InArguments[1], DestinationType); + SecondOpLow = Builder.CreateZExt(InArguments[2], DestinationType); + SecondOpHigh = Builder.CreateZExt(InArguments[3], DestinationType); - FirstOperandHigh = Builder.CreateShl(FirstOperandHigh, RegisterSize); - SecondOperandHigh = Builder.CreateShl(SecondOperandHigh, RegisterSize); + FirstOpHigh = Builder.CreateShl(FirstOpHigh, RegisterSize); + SecondOpHigh = Builder.CreateShl(SecondOpHigh, RegisterSize); - Value *FirstOperand = Builder.CreateOr(FirstOperandHigh, FirstOperandLow); - Value *SecondOperand = Builder.CreateOr(SecondOperandHigh, - SecondOperandLow); + Value *FirstOp = Builder.CreateOr(FirstOpHigh, FirstOpLow); + Value *SecondOp = Builder.CreateOr(SecondOpHigh, + SecondOpLow); Instruction::BinaryOps BinaryOp = opcodeToBinaryOp(Opcode); - Value *Result = Builder.CreateBinOp(BinaryOp, FirstOperand, SecondOperand); + Value *Result = Builder.CreateBinOp(BinaryOp, FirstOp, SecondOp); Value *ResultLow = Builder.CreateTrunc(Result, RegisterType); Value *ShiftedResult = Builder.CreateLShr(Result, RegisterSize); @@ -1303,21 +1378,21 @@ InstructionTranslator::translateOpcode(PTCOpcode Opcode, { IntegerType *DestinationType = Builder.getIntNTy(RegisterSize * 2); - Value *FirstOperand = nullptr; - Value *SecondOperand = nullptr; + Value *FirstOp = nullptr; + Value *SecondOp = nullptr; if (Opcode == PTC_INSTRUCTION_op_mulu2_i32 || Opcode == PTC_INSTRUCTION_op_mulu2_i64) { - FirstOperand = Builder.CreateZExt(InArguments[0], DestinationType); - SecondOperand = Builder.CreateZExt(InArguments[1], DestinationType); + FirstOp = Builder.CreateZExt(InArguments[0], DestinationType); + SecondOp = Builder.CreateZExt(InArguments[1], DestinationType); } else if (Opcode == PTC_INSTRUCTION_op_muls2_i32 || Opcode == PTC_INSTRUCTION_op_muls2_i64) { - FirstOperand = Builder.CreateSExt(InArguments[0], DestinationType); - SecondOperand = Builder.CreateSExt(InArguments[1], DestinationType); + FirstOp = Builder.CreateSExt(InArguments[0], DestinationType); + SecondOp = Builder.CreateSExt(InArguments[1], DestinationType); } else llvm_unreachable("Unexpected opcode"); - Value *Result = Builder.CreateMul(FirstOperand, SecondOperand); + Value *Result = Builder.CreateMul(FirstOp, SecondOp); Value *ResultLow = Builder.CreateTrunc(Result, RegisterType); Value *ShiftedResult = Builder.CreateLShr(Result, RegisterSize); diff --git a/variablemanager.cpp b/variablemanager.cpp index 45cbcee02..68bac0f74 100644 --- a/variablemanager.cpp +++ b/variablemanager.cpp @@ -11,12 +11,14 @@ // LLVM includes #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/Support/Casting.h" // Local includes +#include "debug.h" #include "ir-helpers.h" #include "variablemanager.h" #include "revamb.h" @@ -25,20 +27,48 @@ using namespace llvm; -template -static void pushIfNew(std::set& Seen, std::stack& Queue, T Element) { - if (Seen.find(Element) == Seen.end()) { - Seen.insert(Element); - Queue.push(Element); - } +#ifndef NDEBUG +namespace llvm { +void Value::assertModuleIsMaterialized() const { } } +#endif + +class OffsetValueStack { + +private: + using OffsetValuePair = std::pair; + +public: + void pushIfNew(int64_t Offset, Value *V) { + OffsetValuePair Element = { Offset, V }; + if (!Seen.count(Element)) { + Seen.insert(Element); + Stack.push_back(Element); + } + } + + void push(int64_t Offset, Value *V) { + OffsetValuePair Element = { Offset, V }; + Stack.push_back(Element); + } + + bool empty() { return Stack.empty(); } + + std::pair pop() { + auto Result = Stack.back(); + Stack.pop_back(); + return Result; + } + +private: + std::set Seen; + std::vector Stack; +}; static const int64_t ErrorOffset = std::numeric_limits::max(); bool CorrectCPUStateUsagePass::runOnModule(Module& TheModule) { - using OffsetValuePair = std::pair; - std::set SeenArgs; - std::stack WorkList; + OffsetValueStack WorkList; Value *CPUStatePtr = TheModule.getGlobalVariable("env"); @@ -48,21 +78,27 @@ bool CorrectCPUStateUsagePass::runOnModule(Module& TheModule) { assert(CPUStatePtr->getType()->isPointerTy()); - // Initialize the worklist with all the instructions loading env - for (Use& CPUStateUse : CPUStatePtr->uses()) { - auto *Load = cast(CPUStateUse.getUser()); - assert(Load->getPointerOperand() == CPUStatePtr); - WorkList.push(std::make_pair(Variables->EnvOffset, Load)); - } const DataLayout& DL = TheModule.getDataLayout(); - while (!WorkList.empty()) { + while (true) { + if (WorkList.empty()) { + for (Use& CPUStateUse : CPUStatePtr->uses()) { + auto *Load = cast(CPUStateUse.getUser()); + assert(Load->getPointerOperand() == CPUStatePtr); + + WorkList.pushIfNew(Variables->EnvOffset, Load); + } + } + + if (WorkList.empty()) + break; + + int64_t CurrentOffset; Value *CurrentValue; - std::tie(CurrentOffset, CurrentValue) = WorkList.top(); - WorkList.pop(); + std::tie(CurrentOffset, CurrentValue) = WorkList.pop(); std::vector> Replacements; @@ -74,7 +110,7 @@ bool CorrectCPUStateUsagePass::runOnModule(Module& TheModule) { && Opcode != Instruction::Load && Opcode != Instruction::Store) { // Not loading or storing, propagate the error value - WorkList.push(std::make_pair(ErrorOffset, TheUser)); + WorkList.push(ErrorOffset, TheUser); continue; } @@ -82,42 +118,47 @@ bool CorrectCPUStateUsagePass::runOnModule(Module& TheModule) { case Instruction::Load: case Instruction::Store: { - if (Opcode == Instruction::Store) { - // It's a store, just change the destination pointer - assert(cast(TheUser)->getPointerOperand() == CurrentValue - && "Pointer cannot be used as source of a store instruction"); - } else if (Opcode == Instruction::Load) { - // It's a load, just change the source pointer - assert(cast(TheUser)->getPointerOperand() == CurrentValue - && "Pointer cannot be used as destination of a load" - " instruction"); - } + auto *Load = dyn_cast(TheUser); + auto *Store = dyn_cast(TheUser); - GlobalVariable *Var = Variables->getByCPUStateOffset(CurrentOffset); + IRBuilder<> Builder(cast(TheUser)); - // Couldn't translate this environment usage, make it fail at run-time - if (Var == nullptr) { - auto *InvalidInst = cast(TheUser); - // TODO: emit a warning - CallInst::Create(TheModule.getFunction("abort"), { }, InvalidInst); - // TODO: shall we put an unreachable and delete everything comes - // afterwards? + bool Success = false; + if (Load != nullptr) { + unsigned Size = DL.getTypeSizeInBits(TheUser->getType()) / 8; + assert(Size != 0); + + unsigned CurrentEnvOffset = CurrentOffset - EnvOffset; + auto *Loaded = Variables->loadFromEnvOffset(Builder, + Size, + CurrentEnvOffset); + Success = Loaded != nullptr; + if (Success) + TheUser->replaceAllUsesWith(Loaded); } else { - Constant *Ptr = Var; + Value *ToStore = Store->getValueOperand(); + unsigned Size = DL.getTypeSizeInBits(ToStore->getType()) / 8; + assert(Size != 0); - // Sadly, we have to allow this, mainly due to unions - if (CurrentValue->getType() != Var->getType()) - Ptr = ConstantExpr::getPointerCast(Ptr, CurrentValue->getType()); - - Replacements.push_back(std::make_tuple(TheUser, CurrentValue, Ptr)); + unsigned CurrentEnvOffset = CurrentOffset - EnvOffset; + Success = Variables->storeToEnvOffset(Builder, + Size, + CurrentEnvOffset, + ToStore); } + + if (Success) + Replacements.push_back(std::make_tuple(TheUser, nullptr, nullptr)); + else + Builder.CreateCall(TheModule.getFunction("abort")); + break; } case Instruction::IntToPtr: case Instruction::BitCast: { // A bitcast, just propagate it - WorkList.push(std::make_pair(CurrentOffset, TheUser)); + WorkList.push(CurrentOffset, TheUser); break; } case Instruction::GetElementPtr: @@ -136,7 +177,7 @@ bool CorrectCPUStateUsagePass::runOnModule(Module& TheModule) { } int64_t NewOffset = APOffset.getSExtValue(); - WorkList.push(std::make_pair(CurrentOffset + NewOffset, TheUser)); + WorkList.push(CurrentOffset + NewOffset, TheUser); break; } case Instruction::Add: @@ -151,7 +192,7 @@ bool CorrectCPUStateUsagePass::runOnModule(Module& TheModule) { } int64_t Addend = cast(OtherOperand)->getSExtValue(); - WorkList.push(std::make_pair(CurrentOffset + Addend, TheUser)); + WorkList.push(CurrentOffset + Addend, TheUser); break; } case Instruction::Call: @@ -197,9 +238,7 @@ bool CorrectCPUStateUsagePass::runOnModule(Module& TheModule) { assert(!Callee->isVarArg()); // If not already considered, enqueue the argument to the worklist - pushIfNew(SeenArgs, - WorkList, - std::make_pair(CurrentOffset, TargetArg)); + WorkList.pushIfNew(CurrentOffset, TargetArg); } break; } @@ -210,22 +249,22 @@ bool CorrectCPUStateUsagePass::runOnModule(Module& TheModule) { for (User *FunctionUse : CurrentFunction->users()) { auto Call = cast(FunctionUse); assert(Call->getCalledFunction() == CurrentFunction); - pushIfNew(SeenArgs, - WorkList, - std::make_pair(CurrentOffset, - static_cast(Call))); + WorkList.pushIfNew(CurrentOffset, static_cast(Call)); } break; } default: // Unhandled situation, propagate an error value until the next load - WorkList.push(std::make_pair(ErrorOffset, TheUser)); + WorkList.push(ErrorOffset, TheUser); } } for (auto Replacement : Replacements) - std::get<0>(Replacement)->replaceUsesOfWith(std::get<1>(Replacement), - std::get<2>(Replacement)); + if (std::get<1>(Replacement) == nullptr) + cast(std::get<0>(Replacement))->eraseFromParent(); + else + std::get<0>(Replacement)->replaceUsesOfWith(std::get<1>(Replacement), + std::get<2>(Replacement)); } return true; @@ -238,39 +277,54 @@ static RegisterPass X("correct-cpustate-usage", false, false); -static Type *getTypeAtOffset(const DataLayout *TheLayout, - StructType *TheStruct, - intptr_t Offset) { +static std::pair getTypeAtOffset(const DataLayout *TheLayout, + StructType *TheStruct, + intptr_t Offset, + unsigned Depth=0) { const StructLayout *Layout = TheLayout->getStructLayout(TheStruct); unsigned FieldIndex = Layout->getElementContainingOffset(Offset); uint64_t FieldOffset = Layout->getElementOffset(FieldIndex); - Type *VariableType = TheStruct->getTypeAtIndex(FieldIndex); + intptr_t FieldEnd = FieldOffset + TheLayout->getTypeSizeInBits(VariableType) / 8; + + DBG("type-at-offset", dbg + << std::string(Depth * 2, ' ') + << "Offset: " << Offset << " " + << "Name: " << TheStruct->getName().str() << " " + << "Index: " << FieldIndex << " " + << "Field offset: " << FieldOffset << " " + << "\n"); + + if (Offset >= FieldEnd) + return { nullptr, 0 }; if (VariableType->isIntegerTy()) - return VariableType; + return { VariableType, Offset - FieldOffset }; else if (VariableType->isArrayTy()) { Type *ElementType = VariableType->getArrayElementType(); - if (ElementType->isIntegerTy()) - return ElementType; - uint64_t ElementSize = TheLayout->getTypeSizeInBits(ElementType) / 8; + if (ElementType->isIntegerTy()) + return { ElementType, (Offset - FieldOffset) % ElementSize }; + return getTypeAtOffset(TheLayout, cast(ElementType), - (Offset - FieldOffset) % ElementSize); + (Offset - FieldOffset) % ElementSize, + Depth + 1); } else if (VariableType->isStructTy()) return getTypeAtOffset(TheLayout, cast(VariableType), - Offset - FieldOffset); + Offset - FieldOffset, + Depth + 1); else { // TODO: do some kind of warning reporting here - return nullptr; + return { nullptr, 0 }; } } VariableManager::VariableManager(Module& TheModule, - Module& HelpersModule) : + Module& HelpersModule, + Architecture& TargetArchitecture) : TheModule(TheModule), Builder(TheModule.getContext()), CPUStateType(nullptr), @@ -278,7 +332,8 @@ VariableManager::VariableManager(Module& TheModule, EnvOffset(0), Env(nullptr), AliasScopeMDKindID(TheModule.getMDKindID("alias.scope")), - NoAliasMDKindID(TheModule.getMDKindID("noalias")) { + NoAliasMDKindID(TheModule.getMDKindID("noalias")), + TargetArchitecture(TargetArchitecture) { auto *CPUStateAliasDomain = MDNode::getDistinct(TheModule.getContext(), ArrayRef()); @@ -307,9 +362,9 @@ VariableManager::VariableManager(Module& TheModule, if (ReturnType->isPointerTy()) Structs.insert(dyn_cast(ReturnType->getPointerElementType())); - for (Type *Candidate : HelperType->params()) - if (Candidate->isPointerTy()) - Structs.insert(dyn_cast(Candidate->getPointerElementType())); + for (Type *Param : HelperType->params()) + if (Param->isPointerTy()) + Structs.insert(dyn_cast(Param->getPointerElementType())); if (startsWith(HelperFunction.getName(), HelperPrefix) && HelperFunction.getFunctionType()->getNumParams() > 1) { @@ -374,6 +429,126 @@ VariableManager::VariableManager(Module& TheModule, } } +bool VariableManager::storeToCPUStateOffset(IRBuilder<> &Builder, + unsigned StoreSize, + unsigned Offset, + Value *ToStore) { + Value *Target; + unsigned Remaining; + std::tie(Target, Remaining) = getByCPUStateOffsetInternal(Offset); + + assert(Target != nullptr); + + if (Target == nullptr) + return false; + + unsigned ShiftAmount = 0; + if (TargetArchitecture.isLittleEndian()) + ShiftAmount = Remaining; + else { + // >> (Size1 - Size2) - Remaining; + Type *PointeeTy = Target->getType()->getPointerElementType(); + unsigned GlobalSize = cast(PointeeTy)->getBitWidth() / 8; + assert(GlobalSize != 0); + ShiftAmount = (GlobalSize - StoreSize) - Remaining; + } + ShiftAmount *= 8; + + // Build blanking mask + uint64_t BitMask = (StoreSize == 8 ? + (uint64_t) -1 + : ((uint64_t) 1 << StoreSize * 8) - 1); + assert(ShiftAmount != 64); + BitMask <<= ShiftAmount; + BitMask = ~BitMask; + + auto *InputStoreTy = cast(Builder.getIntNTy(StoreSize * 8)); + auto *FieldTy = cast(Target->getType()->getPointerElementType()); + unsigned FieldSize = FieldTy->getBitWidth() / 8; + + // Truncate value to store + auto *Truncated = Builder.CreateTrunc(ToStore, InputStoreTy); + + // Are we trying to store more than it fits? + if (StoreSize > FieldSize) { + // It's OK as long as after what we're storing there's a hole + assert(getByCPUStateOffsetInternal(Offset + FieldSize).first == nullptr); + Truncated = Builder.CreateTrunc(Truncated, FieldTy); + } + + // Re-extend + ToStore = Builder.CreateZExt(Truncated, FieldTy); + + if (BitMask != 0) { + // Load the value + auto *LoadEnvField = Builder.CreateLoad(Target); + setAliasScope(LoadEnvField); + + auto *Blanked = Builder.CreateAnd(LoadEnvField, BitMask); + + // Shift value to store + ToStore = Builder.CreateShl(ToStore, ShiftAmount); + + // Combine them + ToStore = Builder.CreateOr(ToStore, Blanked); + } + + // Type *TargetPointer = Target->getType()->getPointerElementType(); + // Value *ToStore = Builder.CreateZExt(InArguments[0], TargetPointer); + auto *Store = Builder.CreateStore(ToStore, Target); + setAliasScope(Store); + + return true; +} + +Value *VariableManager::loadFromCPUStateOffset(IRBuilder<> &Builder, + unsigned LoadSize, + unsigned Offset) { + Value *Target; + unsigned Remaining; + std::tie(Target, Remaining) = getByCPUStateOffsetInternal(Offset); + + if (Target == nullptr) + return nullptr; + + // Load the whole field + auto *LoadEnvField = Builder.CreateLoad(Target); + setAliasScope(LoadEnvField); + + // Extract the desired part + // Shift right of the desired amount + unsigned ShiftAmount = 0; + if (TargetArchitecture.isLittleEndian()) + ShiftAmount = Remaining; + else { + // >> (Size1 - Size2) - Remaining; + auto *LoadedTy = cast(LoadEnvField->getType()); + unsigned GlobalSize = LoadedTy->getBitWidth() / 8; + assert(GlobalSize != 0); + ShiftAmount = (GlobalSize - LoadSize) - Remaining; + } + ShiftAmount *= 8; + Value *Result = LoadEnvField; + + if (ShiftAmount != 0) + Result = Builder.CreateLShr(Result, ShiftAmount); + + Type *LoadTy = Builder.getIntNTy(LoadSize * 8); + + // Are we trying to load more than its available in the field? + if (auto FieldTy = dyn_cast(Result->getType())) { + unsigned FieldSize = FieldTy->getBitWidth() / 8; + if (FieldSize < LoadSize) { + // It's OK as long as after what we can't load there's a hole + assert(getByCPUStateOffsetInternal(Offset + FieldSize).first == nullptr); + Result = Builder.CreateZExt(Result, LoadTy); + } + } + + // Truncate of the desired amount + return Builder.CreateTrunc(Result, LoadTy); +} + // TODO: `newFunction` reflects the tcg terminology but in this context is // highly misleading void VariableManager::newFunction(Instruction *Delimiter, @@ -434,19 +609,38 @@ static ConstantInt *fromBytes(IntegerType *Type, void *Data) { // TODO: document that it can return nullptr GlobalVariable* VariableManager::getByCPUStateOffset(intptr_t Offset, std::string Name) { + GlobalVariable *Result = nullptr; + unsigned Remaining; + std::tie(Result, Remaining) = getByCPUStateOffsetInternal(Offset, Name); + assert(Remaining == 0); + return Result; +} + +std::pair +VariableManager::getByCPUStateOffsetInternal(intptr_t Offset, + std::string Name) { if (Offset == ErrorOffset) - return nullptr; + return { nullptr, 0 }; GlobalsMap::iterator it = CPUStateGlobals.find(Offset); if (it == CPUStateGlobals.end() || (Name.size() != 0 && !it->second->getName().equals_lower(Name))) { - Type *VariableType = getTypeAtOffset(ModuleLayout, - CPUStateType, - Offset); + Type *VariableType; + unsigned Remaining; + std::tie(VariableType, Remaining) = getTypeAtOffset(ModuleLayout, + CPUStateType, + Offset); + + // Check we're not trying to go inside an existing variable + if (Remaining != 0) { + GlobalsMap::iterator it = CPUStateGlobals.find(Offset - Remaining); + if (it != CPUStateGlobals.end()) + return { it->second, Remaining }; + } // Unsupported type, let the caller handle the situation if (VariableType == nullptr) - return nullptr; + return { nullptr, 0 }; if (Name.size() == 0) { std::stringstream NameStream; @@ -455,8 +649,8 @@ GlobalVariable* VariableManager::getByCPUStateOffset(intptr_t Offset, } // TODO: offset could be negative, we could segfault here - ConstantInt *InitialValue = fromBytes(cast(VariableType), - ptc.initialized_env - EnvOffset + Offset); + auto *InitialValue = fromBytes(cast(VariableType), + ptc.initialized_env - EnvOffset + Offset); auto *NewVariable = new GlobalVariable(TheModule, VariableType, @@ -473,9 +667,9 @@ GlobalVariable* VariableManager::getByCPUStateOffset(intptr_t Offset, CPUStateGlobals[Offset] = NewVariable; - return NewVariable; + return { NewVariable, Remaining }; } else { - return it->second; + return { it->second, 0 }; } } diff --git a/variablemanager.h b/variablemanager.h index 3113ff4e8..e98210e3e 100644 --- a/variablemanager.h +++ b/variablemanager.h @@ -12,6 +12,7 @@ // Local includes #include "ptcdump.h" +#include "revamb.h" namespace llvm { class AllocaInst; @@ -29,16 +30,22 @@ class CorrectCPUStateUsagePass : public llvm::ModulePass { public: static char ID; - CorrectCPUStateUsagePass() : llvm::ModulePass(ID), Variables(nullptr) { } - CorrectCPUStateUsagePass(VariableManager *Variables) : + CorrectCPUStateUsagePass() : llvm::ModulePass(ID), - Variables(Variables) { } + Variables(nullptr), + EnvOffset(0) { } + + CorrectCPUStateUsagePass(VariableManager *Variables, unsigned EnvOffset) : + llvm::ModulePass(ID), + Variables(Variables), + EnvOffset(EnvOffset) { } public: bool runOnModule(llvm::Module& TheModule) override; private: VariableManager *Variables; + unsigned EnvOffset; }; /// \brief Maintains the list of variables required by PTC. @@ -48,7 +55,8 @@ private: class VariableManager { public: VariableManager(llvm::Module& TheModule, - llvm::Module& HelpersModule); + llvm::Module& HelpersModule, + Architecture &TargetArchitecture); friend class CorrectCPUStateUsagePass; @@ -61,9 +69,10 @@ public: // TODO: rename to getByTemporaryId llvm::Value *getOrCreate(unsigned int TemporaryId); - llvm::GlobalVariable *getByEnvOffset(intptr_t Offset, - std::string Name="") { - return getByCPUStateOffset(EnvOffset + Offset, Name); + std::pair getByEnvOffset(intptr_t Offset, + std::string Name="") { + return getByCPUStateOffsetInternal(EnvOffset + Offset, Name); } /// Informs the VariableManager that a new function has begun, so it can @@ -91,7 +100,7 @@ public: bool isEnv(llvm::Value *TheValue); CorrectCPUStateUsagePass *createCorrectCPUStateUsagePass() { - return new CorrectCPUStateUsagePass(this); + return new CorrectCPUStateUsagePass(this, EnvOffset); } llvm::Value *computeEnvAddress(llvm::Type *TargetType, @@ -115,9 +124,36 @@ public: return Locals; } + llvm::Value *loadFromEnvOffset(llvm::IRBuilder<> &Builder, + unsigned LoadSize, + unsigned Offset) { + return loadFromCPUStateOffset(Builder, LoadSize, EnvOffset + Offset); + } + + bool storeToEnvOffset(llvm::IRBuilder<> &Builder, + unsigned StoreSize, + unsigned Offset, + llvm::Value *ToStore) { + unsigned ActualOffset = EnvOffset + Offset; + return storeToCPUStateOffset(Builder, StoreSize, ActualOffset, ToStore); + } + private: + llvm::Value *loadFromCPUStateOffset(llvm::IRBuilder<> &Builder, + unsigned LoadSize, + unsigned Offset); + + bool storeToCPUStateOffset(llvm::IRBuilder<> &Builder, + unsigned StoreSize, + unsigned Offset, + llvm::Value *ToStore); + llvm::GlobalVariable *getByCPUStateOffset(intptr_t Offset, std::string Name=""); + std::pair + getByCPUStateOffsetInternal(intptr_t Offset, + std::string Name=""); + private: llvm::Module& TheModule; llvm::IRBuilder<> Builder; @@ -137,6 +173,8 @@ private: unsigned AliasScopeMDKindID; unsigned NoAliasMDKindID; llvm::MDNode *CPUStateScopeSet; + + Architecture &TargetArchitecture; }; #endif // _VARIABLEMANAGER_H