summaryrefslogtreecommitdiffstats
path: root/src/llvm/llvm-opc-mmu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/llvm/llvm-opc-mmu.cpp')
-rw-r--r--src/llvm/llvm-opc-mmu.cpp344
1 files changed, 344 insertions, 0 deletions
diff --git a/src/llvm/llvm-opc-mmu.cpp b/src/llvm/llvm-opc-mmu.cpp
new file mode 100644
index 0000000..9d2e60f
--- /dev/null
+++ b/src/llvm/llvm-opc-mmu.cpp
@@ -0,0 +1,344 @@
+/*
+ * (C) 2015 by Computer System Laboratory, IIS, Academia Sinica, Taiwan.
+ * See COPYRIGHT in top-level directory.
+ *
+ * This file provides LLVM IR generator in terms of basic block and trace.
+ */
+
+#include "llvm-debug.h"
+#include "llvm.h"
+#include "llvm-opc.h"
+#include "llvm-target.h"
+#include "utils.h"
+
+#if defined(CONFIG_SOFTMMU)
+extern "C" {
+extern const void * const llvm_ld_helpers[16];
+extern const void * const llvm_st_helpers[16];
+};
+#endif
+
+
+#if defined(CONFIG_USER_ONLY)
+Value *IRFactory::QEMULoad(Value *AddrL, Value *AddrH, TCGMemOpIdx oi)
+{
+ TCGMemOp opc = get_memop(oi);
+ Value *Base = AddrL;
+ PointerType *PtrTy = getPointerTy(getSizeInBits(opc), Segment);
+ LoadInst *LI;
+
+ if (GUEST_BASE == 0 || Segment != 0) {
+ Base = ITP(Base, PtrTy);
+ LI = new LoadInst(Base, "", true, LastInst);
+ } else {
+ Base = ITP(Base, Int8PtrTy);
+ Base = GetElementPtrInst::CreateInBounds(Base, GuestBaseReg.Base, "", LastInst);
+ if (Base->getType() != PtrTy)
+ Base = CAST(Base, PtrTy);
+ LI = new LoadInst(Base, "", true, LastInst);
+ }
+ MF->setGuestMemory(LI);
+
+ return ConvertEndian(LI, opc);
+}
+
+void IRFactory::QEMUStore(Value *Data, Value *AddrL, Value *AddrH, TCGMemOpIdx oi)
+{
+ TCGMemOp opc = get_memop(oi);
+ Value *Base = AddrL;
+ PointerType *PtrTy = getPointerTy(getSizeInBits(opc), Segment);
+ StoreInst *SI;
+
+ Data = ConvertEndian(Data, opc);
+
+ if (GUEST_BASE == 0 || Segment != 0) {
+ Base = ITP(Base, PtrTy);
+ SI = new StoreInst(Data, Base, true, LastInst);
+ } else {
+ Base = ITP(Base, Int8PtrTy);
+ Base = GetElementPtrInst::CreateInBounds(Base, GuestBaseReg.Base, "", LastInst);
+ if (Base->getType() != PtrTy)
+ Base = CAST(Base, PtrTy);
+ SI = new StoreInst(Data, Base, true, LastInst);
+ }
+ MF->setGuestMemory(SI);
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+inline long getTLBOffset(int mem_index)
+{
+ long Offset = 0;
+
+ switch (mem_index) {
+#if NB_MMU_MODES > 0
+ case 0: Offset = offsetof(CPUArchState, tlb_table[0][0]); break;
+#endif
+#if NB_MMU_MODES > 1
+ case 1: Offset = offsetof(CPUArchState, tlb_table[1][0]); break;
+#endif
+#if NB_MMU_MODES > 2
+ case 2: Offset = offsetof(CPUArchState, tlb_table[2][0]); break;
+#endif
+#if NB_MMU_MODES > 3
+ case 3: Offset = offsetof(CPUArchState, tlb_table[3][0]); break;
+#endif
+#if NB_MMU_MODES > 4
+ case 4: Offset = offsetof(CPUArchState, tlb_table[4][0]); break;
+#endif
+#if NB_MMU_MODES > 5
+ case 5: Offset = offsetof(CPUArchState, tlb_table[5][0]);
+#endif
+ default:
+ IRError("%s: internal error. mem_index=%d\n", __func__, mem_index);
+ }
+
+ return Offset;
+}
+
+Value *IRFactory::ConcatTLBVersion(Value *GVA)
+{
+#if defined(ENABLE_TLBVERSION_EXT)
+ GVA = ZEXT64(GVA);
+#endif
+ Type *PtrTy = getPointerTy(DL->getTypeSizeInBits(GVA->getType()));
+ Value *TLBVersion = GetElementPtrInst::CreateInBounds(CPU,
+ CONSTPtr(offsetof(CPUArchState, tlb_version)), "", LastInst);
+ TLBVersion = new BitCastInst(TLBVersion, PtrTy, "", LastInst);
+ TLBVersion = new LoadInst(TLBVersion, "version", true, LastInst);
+ return OR(GVA, TLBVersion);
+}
+
+Value *IRFactory::QEMULoad(Value *AddrL, Value *AddrH, TCGMemOpIdx oi)
+{
+ TCGMemOp opc = get_memop(oi);
+ int mem_index = get_mmuidx(oi);
+ IntegerType *AccessTy;
+ PointerType *GuestPtrTy, *HostPtrTy;
+ int Size, s_bits = opc & MO_SIZE;
+
+ Size = 8 * 1 << s_bits; /* data size (bits) for this load */
+
+ const void *helper = llvm_ld_helpers[opc & (MO_BSWAP | MO_SIZE)];
+ Function *MissFunc = ResolveFunction(getMMUFName(helper));
+ if (!MissFunc)
+ IRError("%s: internal error.\n", __func__);
+
+ GuestPtrTy = (TARGET_LONG_BITS == 32) ? Int32PtrTy : Int64PtrTy;
+ HostPtrTy = (TCG_TARGET_REG_BITS == 32) ? Int32PtrTy : Int64PtrTy;
+
+#if defined(ENABLE_TLBVERSION_EXT)
+ GuestPtrTy = Int64PtrTy;
+#endif
+
+ /* Create TLB basic blocks. */
+ BasicBlock *tlb_hit = BasicBlock::Create(*Context, "tlb_hit", Func);
+ BasicBlock *tlb_miss = BasicBlock::Create(*Context, "tlb_miss", Func);
+ BasicBlock *tlb_exit = BasicBlock::Create(*Context, "tlb_exit", Func);
+ toSink.push_back(tlb_miss);
+
+ /* Load compared value in TLB. QEMU uses only addrlo to index the TLB entry. */
+ Value *TLBEntry, *TLBValue, *CPUAddr;
+ AccessTy = (TCG_TARGET_REG_BITS == 64 && TARGET_LONG_BITS == 64) ? Int64Ty : Int32Ty;
+ size_t Offset = getTLBOffset(mem_index) + offsetof(CPUTLBEntry, addr_read);
+ TLBEntry = LSHR(AddrL, ConstantInt::get(AccessTy, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS));
+ TLBEntry = AND(TLBEntry, ConstantInt::get(AccessTy, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS));
+ TLBEntry = ADD(TLBEntry, ConstantInt::get(AccessTy, Offset));
+
+ if (TLBEntry->getType() != IntPtrTy)
+ TLBEntry = new ZExtInst(TLBEntry, IntPtrTy, "", LastInst);
+
+ CPUAddr = new PtrToIntInst(CPU, IntPtrTy, "", LastInst);
+ TLBEntry = ADD(CPUAddr, TLBEntry);
+ TLBValue = new IntToPtrInst(TLBEntry, GuestPtrTy, "", LastInst);
+ TLBValue = new LoadInst(TLBValue, "tlb.read", false, LastInst);
+
+ /* Compare GVA and TLB value. */
+ Value *GVA, *Cond, *GuestPC = AddrL;
+ AccessTy = (TARGET_LONG_BITS == 32) ? Int32Ty : Int64Ty;
+ if (AddrH) { /* guest is 64-bit and host is 32-bit. */
+ GuestPC = SHL(ZEXT64(AddrH), CONST64(32));
+ GuestPC = OR(GuestPC, ZEXT64(AddrL));
+ }
+#if defined(ALIGNED_ONLY)
+ GVA = AND(GuestPC, ConstantInt::get(AccessTy,
+ TARGET_PAGE_MASK | ((1 << s_bits) - 1)));
+#elif defined(ENABLE_TLBVERSION)
+ GVA = ADD(GuestPC, ConstantInt::get(AccessTy, (1 << s_bits) - 1));
+ GVA = AND(GVA, ConstantInt::get(AccessTy, TARGET_PAGE_MASK));
+ GVA = ConcatTLBVersion(GVA);
+#else
+ GVA = ADD(GuestPC, ConstantInt::get(AccessTy, (1 << s_bits) - 1));
+ GVA = AND(GVA, ConstantInt::get(AccessTy, TARGET_PAGE_MASK));
+#endif
+ Cond = ICMP(GVA, TLBValue, ICmpInst::ICMP_EQ);
+ BranchInst::Create(tlb_hit, tlb_miss, Cond, LastInst);
+ LastInst->eraseFromParent();
+
+ /* TLB hit. */
+ Value *PhyAddr, *Addend, *HitData, *Addr=AddrL;
+
+ LastInst = BranchInst::Create(tlb_exit, tlb_hit);
+ if (Addr->getType() != IntPtrTy)
+ Addr = new ZExtInst(Addr, IntPtrTy, "", LastInst);
+
+ Offset = offsetof(CPUTLBEntry, addend) - offsetof(CPUTLBEntry, addr_read);
+ Addend = ADD(TLBEntry, ConstantInt::get(IntPtrTy, Offset));
+ Addend = new IntToPtrInst(Addend, HostPtrTy, "", LastInst);
+ Addend = new LoadInst(Addend, "tlb.addend", false, LastInst);
+ PhyAddr = ADD(Addr, Addend);
+ PhyAddr = ITP(PhyAddr, getPointerTy(Size));
+ HitData = new LoadInst(PhyAddr, "hit", true, LastInst);
+
+ HitData = ConvertEndian(HitData, opc);
+
+ /* TLB miss. */
+ LastInst = BranchInst::Create(tlb_exit, tlb_miss);
+ SmallVector<Value *, 4> Params;
+ uint32_t restore_val = setRestorePoint(oi);
+ Params.push_back(CPUStruct);
+ Params.push_back(GuestPC);
+ Params.push_back(CONST32(restore_val));
+
+ CallInst *MissCall = CallInst::Create(MissFunc, Params, "", LastInst);
+ Value *MissData = MissCall;
+ switch (opc & MO_SSIZE) {
+ case MO_UB:
+ case MO_SB:
+ if (DL->getTypeSizeInBits(MissData->getType()) != 8)
+ MissData = TRUNC8(MissCall);
+ break;
+ case MO_UW:
+ case MO_SW:
+ if (DL->getTypeSizeInBits(MissData->getType()) != 16)
+ MissData = TRUNC16(MissCall);
+ break;
+ case MO_UL:
+ case MO_SL:
+ if (DL->getTypeSizeInBits(MissData->getType()) != 32)
+ MissData = TRUNC32(MissCall);
+ break;
+ case MO_Q:
+ if (DL->getTypeSizeInBits(MissData->getType()) != 64)
+ MissData = ZEXT64(MissCall);
+ break;
+ default:
+ IRError("%s: invalid size (opc=%d)\n", __func__, opc);
+ break;
+ }
+
+ /* TLB exit. */
+ CurrBB = tlb_exit;
+ LastInst = BranchInst::Create(ExitBB, CurrBB);
+ PHINode *PH = PHINode::Create(HitData->getType(), 2, "", LastInst);
+ PH->addIncoming(HitData, tlb_hit);
+ PH->addIncoming(MissData, tlb_miss);
+
+ return PH;
+}
+
+void IRFactory::QEMUStore(Value *Data, Value *AddrL, Value *AddrH, TCGMemOpIdx oi)
+{
+ TCGMemOp opc = get_memop(oi);
+ int mem_index = get_mmuidx(oi);
+ IntegerType *AccessTy;
+ PointerType *GuestPtrTy, *HostPtrTy;
+ int Size, s_bits = opc & MO_SIZE;
+
+ Size = 8 * 1 << s_bits; /* data size (bits) for this load */
+
+ const void *helper = llvm_st_helpers[opc & (MO_BSWAP | MO_SIZE)];
+ Function *MissFunc = ResolveFunction(getMMUFName(helper));
+ if (!MissFunc)
+ IRError("%s: internal error.\n", __func__);
+
+ GuestPtrTy = (TARGET_LONG_BITS == 32) ? Int32PtrTy : Int64PtrTy;
+ HostPtrTy = (TCG_TARGET_REG_BITS == 32) ? Int32PtrTy : Int64PtrTy;
+
+#if defined(ENABLE_TLBVERSION_EXT)
+ GuestPtrTy = Int64PtrTy;
+#endif
+
+ /* Create TLB basic blocks. */
+ BasicBlock *tlb_hit = BasicBlock::Create(*Context, "tlb_hit", Func);
+ BasicBlock *tlb_miss = BasicBlock::Create(*Context, "tlb_miss", Func);
+ BasicBlock *tlb_exit = BasicBlock::Create(*Context, "tlb_exit", Func);
+ toSink.push_back(tlb_miss);
+
+ /* Load compared value in TLB. QEMU uses only addrlo to index the TLB entry. */
+ Value *TLBEntry, *TLBValue, *CPUAddr;
+ AccessTy = (TCG_TARGET_REG_BITS == 64 && TARGET_LONG_BITS == 64) ? Int64Ty : Int32Ty;
+ size_t Offset = getTLBOffset(mem_index) + offsetof(CPUTLBEntry, addr_write);
+ TLBEntry = LSHR(AddrL, ConstantInt::get(AccessTy, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS));
+ TLBEntry = AND(TLBEntry, ConstantInt::get(AccessTy, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS));
+ TLBEntry = ADD(TLBEntry, ConstantInt::get(AccessTy, Offset));
+
+ if (TLBEntry->getType() != IntPtrTy)
+ TLBEntry = new ZExtInst(TLBEntry, IntPtrTy, "", LastInst);
+
+ CPUAddr = new PtrToIntInst(CPU, IntPtrTy, "", LastInst);
+ TLBEntry = ADD(CPUAddr, TLBEntry);
+ TLBValue = new IntToPtrInst(TLBEntry, GuestPtrTy, "", LastInst);
+ TLBValue = new LoadInst(TLBValue, "tlb.write", false, LastInst);
+
+ /* Compare GVA and TLB value. */
+ Value *GVA, *Cond, *GuestPC = AddrL;
+ AccessTy = (TARGET_LONG_BITS == 32) ? Int32Ty : Int64Ty;
+ if (AddrH != nullptr) { /* guest is 64-bit and host is 32-bit. */
+ GuestPC = SHL(ZEXT64(AddrH), CONST64(32));
+ GuestPC = OR(GuestPC, ZEXT64(AddrL));
+ }
+#if defined(ALIGNED_ONLY)
+ GVA = AND(GuestPC, ConstantInt::get(AccessTy,
+ TARGET_PAGE_MASK | ((1 << s_bits) - 1)));
+#elif defined(ENABLE_TLBVERSION)
+ GVA = ADD(GuestPC, ConstantInt::get(AccessTy, (1 << s_bits) - 1));
+ GVA = AND(GVA, ConstantInt::get(AccessTy, TARGET_PAGE_MASK));
+ GVA = ConcatTLBVersion(GVA);
+#else
+ GVA = ADD(GuestPC, ConstantInt::get(AccessTy, (1 << s_bits) - 1));
+ GVA = AND(GVA, ConstantInt::get(AccessTy, TARGET_PAGE_MASK));
+#endif
+ Cond = ICMP(GVA, TLBValue, ICmpInst::ICMP_EQ);
+ BranchInst::Create(tlb_hit, tlb_miss, Cond, LastInst);
+ LastInst->eraseFromParent();
+
+ /* TLB hit. */
+ Value *PhyAddr, *Addend, *Addr=AddrL;
+
+ LastInst = BranchInst::Create(tlb_exit, tlb_hit);
+ if (Addr->getType() != IntPtrTy)
+ Addr = new ZExtInst(Addr, IntPtrTy, "", LastInst);
+
+ Offset = offsetof(CPUTLBEntry, addend) - offsetof(CPUTLBEntry, addr_write);
+ Addend = ADD(TLBEntry, ConstantInt::get(IntPtrTy, Offset));
+ Addend = new IntToPtrInst(Addend, HostPtrTy, "", LastInst);
+ Addend = new LoadInst(Addend, "tlb.addend", false, LastInst);
+ PhyAddr = ADD(Addr, Addend);
+ PhyAddr = ITP(PhyAddr, getPointerTy(Size));
+
+ Value *HitData = ConvertEndian(Data, opc);
+
+ new StoreInst(HitData, PhyAddr, true, LastInst);
+
+ /* TLB miss. */
+ LastInst = BranchInst::Create(tlb_exit, tlb_miss);
+ SmallVector<Value *, 4> Params;
+ uint32_t restore_val = setRestorePoint(oi);
+ Params.push_back(CPUStruct);
+ Params.push_back(GuestPC);
+ Params.push_back(Data);
+ Params.push_back(CONST32(restore_val));
+
+ CallInst::Create(MissFunc, Params, "", LastInst);
+
+ /* TLB exit. */
+ CurrBB = tlb_exit;
+ LastInst = BranchInst::Create(ExitBB, CurrBB);
+}
+
+#endif /* CONFIG_USER_ONLY */
+
+/*
+ * vim: ts=8 sts=4 sw=4 expandtab
+ */
OpenPOWER on IntegriCloud