Chromium Code Reviews| Index: src/IceAssemblerMIPS32.cpp |
| diff --git a/src/IceAssemblerMIPS32.cpp b/src/IceAssemblerMIPS32.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2666b4acb96f98233873493bc2d01f132f1f9b00 |
| --- /dev/null |
| +++ b/src/IceAssemblerMIPS32.cpp |
| @@ -0,0 +1,423 @@ |
| +//===- subzero/src/IceAssemblerMIPS32.cpp - Assembler for MIPS32 --*- C++ -*-===// |
|
Jim Stichnoth
2016/08/19 17:12:21
Fix 80-col here. May be easiest to change the des
|
| +// |
| +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
|
Jim Stichnoth
2016/08/19 17:12:21
For the x86 and ARM32 integrated assemblers, we st
|
| +// 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. |
| +// |
| +// Modified by the Subzero authors. |
| +// |
| +//===----------------------------------------------------------------------===// |
| +// |
| +// The Subzero Code Generator |
| +// |
| +// This file is distributed under the University of Illinois Open Source |
| +// License. See LICENSE.TXT for details. |
| +// |
| +//===----------------------------------------------------------------------===// |
| +/// |
| +/// \file |
| +/// \brief Implements the Assembler class for MIPS32. |
| +/// |
| +//===----------------------------------------------------------------------===// |
| + |
| +#include "IceAssemblerMIPS32.h" |
| +#include "IceRegistersMIPS32.h" |
| +#include "IceCfgNode.h" |
|
Jim Stichnoth
2016/08/19 17:12:21
Can these be sorted alphabetically?
|
| +#include "IceUtils.h" |
| + |
| +namespace { |
| + |
| +using namespace Ice; |
| +using namespace Ice::MIPS32; |
| + |
| +//using WordType = uint32_t; |
|
Jim Stichnoth
2016/08/19 17:12:21
Remove commented-out code?
|
| +//static constexpr IValueT kWordSize = sizeof(WordType); |
| + |
| +// Offset modifier to current PC for next instruction. |
| +static constexpr IOffsetT kPCReadOffset = 4; |
| + |
| +// Mask to pull out PC offset from branch instruction. |
| +static constexpr int kBranchOffsetBits = 16; |
| +static constexpr IOffsetT kBranchOffsetMask = 0x0000ffff; |
| + |
| +} // end of anonymous namespace |
| + |
| +namespace Ice { |
| +namespace MIPS32 { |
| + |
| +void AssemblerMIPS32::emitTextInst(const std::string &Text, SizeT InstSize) { |
| + AssemblerFixup *F = createTextFixup(Text, InstSize); |
| + emitFixup(F); |
| + for (SizeT I = 0; I < InstSize; ++I) { |
| + AssemblerBuffer::EnsureCapacity ensured(&Buffer); |
| + Buffer.emit<char>(0); |
| + } |
| +} |
| + |
| +namespace { |
| + |
| +// TEQ $0, $0 - Trap if equal |
| +const uint8_t TrapBytesRaw[] = {0x00, 0x00, 0x00, 0x34}; |
|
Jim Stichnoth
2016/08/19 17:12:21
Can this be constexpr?
|
| + |
| +const auto TrapBytes = |
| + llvm::ArrayRef<uint8_t>(TrapBytesRaw, llvm::array_lengthof(TrapBytesRaw)); |
| + |
| +} // end of anonymous namespace |
| + |
| +llvm::ArrayRef<uint8_t> AssemblerMIPS32::getNonExecBundlePadding() const { |
| + return TrapBytes; |
| +} |
| + |
| +void AssemblerMIPS32::trap() { |
| + AssemblerBuffer::EnsureCapacity ensured(&Buffer); |
| + for (const uint8_t &Byte : reverse_range(TrapBytes)) |
| + Buffer.emit<uint8_t>(Byte); |
| +} |
| + |
| +void AssemblerMIPS32::nop() { |
| + emitInst(0); |
| +} |
| + |
| +void AssemblerMIPS32::padWithNop(intptr_t Padding) { |
| + constexpr intptr_t InstWidth = sizeof(IValueT); |
| + assert(Padding % InstWidth == 0 && |
| + "Padding not multiple of instruction size"); |
| + for (intptr_t i = 0; i < Padding; i += InstWidth) |
| + nop(); |
| +} |
| + |
| +Label *AssemblerMIPS32::getOrCreateLabel(SizeT Number, LabelVector &Labels) { |
| + Label *L = nullptr; |
| + if (Number == Labels.size()) { |
| + L = new (this->allocate<Label>()) Label(); |
| + Labels.push_back(L); |
| + return L; |
| + } |
| + if (Number > Labels.size()) { |
| + Labels.resize(Number + 1); |
| + } |
| + L = Labels[Number]; |
| + if (!L) { |
|
Jim Stichnoth
2016/08/19 17:12:21
L != nullptr
|
| + L = new (this->allocate<Label>()) Label(); |
| + Labels[Number] = L; |
| + } |
| + return L; |
| +} |
| + |
| +void AssemblerMIPS32::bindCfgNodeLabel(const CfgNode *Node) { |
| + if (BuildDefs::dump() && !getFlags().getDisableHybridAssembly()) { |
| + constexpr SizeT InstSize = 0; |
| + emitTextInst(Node->getAsmName() + ":", InstSize); |
| + } |
| + SizeT NodeNumber = Node->getIndex(); |
| + assert(!getPreliminary()); |
| + Label *L = getOrCreateCfgNodeLabel(NodeNumber); |
| + this->bind(L); |
| +} |
| + |
| +// Checks that Offset can fit in imm16 constant of branch instruction. |
| +void assertCanEncodeBranchOffset(IOffsetT Offset) { |
| + (void)Offset; |
| + (void)kBranchOffsetBits; |
| + assert(Utils::IsAligned(Offset, 4) && |
|
Jim Stichnoth
2016/08/19 17:12:21
Instead of:
assert(A & B);
use:
assert(A);
a
|
| + Utils::IsInt(kBranchOffsetBits, Offset >> 2)); |
| +} |
| + |
| +IValueT encodeBranchOffset(IOffsetT Offset, IValueT Inst) { |
| + Offset -= kPCReadOffset; |
| + assertCanEncodeBranchOffset(Offset); |
| + Offset >>= 2; |
| + Offset &= kBranchOffsetMask; |
| + return (Inst & ~kBranchOffsetMask) | Offset; |
| +} |
| + |
| +IOffsetT AssemblerMIPS32::decodeBranchOffset(IValueT Inst) { |
| + int16_t imm = (Inst & kBranchOffsetMask); |
| + IOffsetT Offset = imm; |
| + Offset = Offset << 2; |
| + return (Offset + kPCReadOffset); |
| +} |
| + |
| +void AssemblerMIPS32::bind(Label *L) { |
| + IOffsetT BoundPc = Buffer.size(); |
| + assert(!L->isBound()); // Labels can only be bound once. |
| + while (L->isLinked()) { |
| + IOffsetT Position = L->getLinkPosition(); |
| + IOffsetT Dest = BoundPc - Position; |
| + IValueT Inst = Buffer.load<IValueT>(Position); |
| + Buffer.store<IValueT>(Position, encodeBranchOffset(Dest, Inst)); |
| + L->setPosition(decodeBranchOffset(Inst)); |
| + } |
| + L->bindTo(BoundPc); |
| +} |
| + |
| +enum RegSetWanted { WantGPRegs, WantFPRegs }; |
| + |
| +IValueT getEncodedGPRegNum(const Variable *Var) { |
| + assert(Var->hasReg()); |
| + const auto Reg = Var->getRegNum(); |
| + return RegMIPS32::getEncodedGPR(Reg); |
| +} |
| + |
| +bool encodeOperand(const Operand *Opnd, IValueT &Value, |
| + RegSetWanted WantedRegSet) { |
| + Value = 0; |
| + if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) { |
| + if (Var->hasReg()) { |
| + switch (WantedRegSet) { |
| + case WantGPRegs: |
| + Value = getEncodedGPRegNum(Var); |
| + break; |
| + default: |
| + break; |
| + } |
| + return true; |
| + } |
| + return false; |
| + } |
| + return false; |
| +} |
| + |
| +IValueT encodeRegister(const Operand *OpReg, RegSetWanted WantedRegSet, |
| + const char *RegName, const char *InstName) { |
| + IValueT Reg = 0; |
| + if (encodeOperand(OpReg, Reg, WantedRegSet) != true) |
| + llvm::report_fatal_error(std::string(InstName) + ": Can't find register " + |
| + RegName); |
| + return Reg; |
| +} |
| + |
| +IValueT encodeGPRegister(const Operand *OpReg, const char *RegName, |
| + const char *InstName) { |
| + return encodeRegister(OpReg, WantGPRegs, RegName, InstName); |
| +} |
| + |
| +void AssemblerMIPS32::emitRtRsImm16(IValueT Opcode, const Operand *OpRt, |
| + const Operand *OpRs, const uint32_t Imm, |
| + const char *InsnName) |
| +{ |
| + IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); |
|
Jim Stichnoth
2016/08/19 17:12:21
const IValueT ...
here and wherever reasonable be
|
| + IValueT Rs = encodeGPRegister(OpRs, "Rs", InsnName); |
| + |
| + Opcode |= Rs << 21; |
| + Opcode |= Rt << 16; |
| + Opcode |= Imm & 0xffff; |
| + |
| + emitInst(Opcode); |
| +} |
| + |
| +void AssemblerMIPS32::emitRdRtSa(IValueT Opcode, const Operand *OpRd, |
| + const Operand *OpRt, const uint32_t Sa, |
| + const char *InsnName) |
| +{ |
| + IValueT Rd = encodeGPRegister(OpRd, "Rd", InsnName); |
| + IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); |
| + |
| + Opcode |= Rt << 16; |
| + Opcode |= Rd << 11; |
| + Opcode |= (Sa & 0x1f) << 6; |
| + |
| + emitInst(Opcode); |
| +} |
| + |
| +void AssemblerMIPS32::emitRdRsRt(IValueT Opcode, const Operand *OpRd, |
| + const Operand *OpRs, const Operand *OpRt, |
| + const char *InsnName) |
| +{ |
| + IValueT Rd = encodeGPRegister(OpRd, "Rd", InsnName); |
| + IValueT Rs = encodeGPRegister(OpRs, "Rs", InsnName); |
| + IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); |
| + |
| + Opcode |= Rs << 21; |
| + Opcode |= Rt << 16; |
| + Opcode |= Rd << 11; |
| + |
| + emitInst(Opcode); |
| +} |
| + |
| +void AssemblerMIPS32::addiu(const Operand *OpRt, const Operand *OpRs, |
| + const uint32_t Imm) |
| +{ |
| + IValueT Opcode = 0x24000000; |
|
Jim Stichnoth
2016/08/19 17:12:21
Here and below, for the Opcode declarations, use s
|
| + emitRtRsImm16 (Opcode, OpRt, OpRs, Imm, "addiu"); |
| +} |
| + |
| +void AssemblerMIPS32::slti(const Operand *OpRt, const Operand *OpRs, |
| + const uint32_t Imm) |
| +{ |
| + IValueT Opcode = 0x28000000; |
| + emitRtRsImm16 (Opcode, OpRt, OpRs, Imm, "slti"); |
| +} |
| + |
| +void AssemblerMIPS32::sltiu(const Operand *OpRt, const Operand *OpRs, |
| + const uint32_t Imm) |
| +{ |
| + IValueT Opcode = 0x2c000000; |
| + emitRtRsImm16 (Opcode, OpRt, OpRs, Imm, "sltiu"); |
| +} |
| + |
| +void AssemblerMIPS32::andi(const Operand *OpRt, const Operand *OpRs, |
| + const uint32_t Imm) |
| +{ |
| + IValueT Opcode = 0x30000000; |
| + emitRtRsImm16 (Opcode, OpRt, OpRs, Imm, "andi"); |
| +} |
| + |
| +void AssemblerMIPS32::ori(const Operand *OpRt, const Operand *OpRs, |
| + const uint32_t Imm) |
| +{ |
| + IValueT Opcode = 0x34000000; |
| + emitRtRsImm16 (Opcode, OpRt, OpRs, Imm, "ori"); |
| +} |
| + |
| +void AssemblerMIPS32::xori(const Operand *OpRt, const Operand *OpRs, |
| + const uint32_t Imm) |
| +{ |
| + IValueT Opcode = 0x38000000; |
| + emitRtRsImm16 (Opcode, OpRt, OpRs, Imm, "xori"); |
| +} |
| + |
| +void AssemblerMIPS32::sll(const Operand *OpRd, const Operand *OpRt, |
| + const uint32_t Sa) |
| +{ |
| + IValueT Opcode = 0x00000000; |
| + emitRdRtSa (Opcode, OpRd, OpRt, Sa, "sll"); |
| +} |
| + |
| +void AssemblerMIPS32::srl(const Operand *OpRd, const Operand *OpRt, |
| + const uint32_t Sa) |
| +{ |
| + IValueT Opcode = 0x00000002; |
| + emitRdRtSa (Opcode, OpRd, OpRt, Sa, "srl"); |
| +} |
| + |
| +void AssemblerMIPS32::sra(const Operand *OpRd, const Operand *OpRt, |
| + const uint32_t Sa) |
| +{ |
| + IValueT Opcode = 0x00000003; |
| + emitRdRtSa (Opcode, OpRd, OpRt, Sa, "sra"); |
| +} |
| + |
| +void AssemblerMIPS32::addu(const Operand *OpRd, const Operand *OpRs, |
| + const Operand *OpRt) |
| +{ |
| + IValueT Opcode = 0x00000021; |
| + emitRdRsRt (Opcode, OpRd, OpRs, OpRt, "addu"); |
| +} |
| + |
| +void AssemblerMIPS32::slt(const Operand *OpRd, const Operand *OpRs, |
| + const Operand *OpRt) |
| +{ |
| + IValueT Opcode = 0x0000002A; |
| + emitRdRsRt (Opcode, OpRd, OpRs, OpRt, "slt"); |
| +} |
| + |
| +void AssemblerMIPS32::sw(const Operand *OpRt, const Operand *OpBase, |
| + const uint32_t Offset) |
| +{ |
| + IValueT Opcode = 0xAC000000; |
| + emitRtRsImm16 (Opcode, OpRt, OpBase, Offset, "sw"); |
| +} |
| + |
| +void AssemblerMIPS32::lw(const Operand *OpRt, const Operand *OpBase, |
| + const uint32_t Offset) |
| +{ |
| + IValueT Opcode = 0x8C000000; |
| + emitRtRsImm16 (Opcode, OpRt, OpBase, Offset, "lw"); |
| +} |
| + |
| +void AssemblerMIPS32::ret(void) |
| +{ |
| + IValueT Opcode = 0x03E00008; // JR $31 |
| + emitInst(Opcode); |
| + nop(); // delay slot |
| +} |
| + |
| +void AssemblerMIPS32::emitBr(const CondMIPS32::Cond Cond, |
| + const Operand *OpRs, const Operand *OpRt, |
| + IOffsetT Offset) |
| +{ |
| + IValueT Opcode = 0; |
| + |
| + switch (Cond) { |
| + default: |
| + break; |
|
Jim Stichnoth
2016/08/19 17:12:21
Is Opcode==0 ever valid here? If not, to be safe,
|
| + case CondMIPS32::AL: |
| + case CondMIPS32::EQ: |
| + case CondMIPS32::EQZ: |
| + Opcode = 0x10000000; |
| + break; |
| + case CondMIPS32::NE: |
| + case CondMIPS32::NEZ: |
| + Opcode = 0x14000000; |
| + break; |
| + case CondMIPS32::LEZ: |
| + Opcode = 0x18000000; |
| + break; |
| + case CondMIPS32::LTZ: |
| + Opcode = 0x04000000; |
| + break; |
| + case CondMIPS32::GEZ: |
| + Opcode = 0x04010000; |
| + break; |
| + case CondMIPS32::GTZ: |
| + Opcode = 0x1C000000; |
| + break; |
| + } |
| + |
| + if (OpRs != nullptr) { |
| + IValueT Rs = encodeGPRegister(OpRs, "Rs", "branch"); |
| + Opcode |= Rs << 21; |
| + } |
| + |
| + if (OpRt != nullptr) { |
| + IValueT Rt = encodeGPRegister(OpRt, "Rt", "branch"); |
| + Opcode |= Rt << 16; |
| + } |
| + |
| + Opcode = encodeBranchOffset(Offset, Opcode); |
| + emitInst(Opcode); |
| + nop(); // delay slot |
| +} |
| + |
| +void AssemblerMIPS32::b(Label *TargetLabel) |
| +{ |
| + if (TargetLabel->isBound()) { |
| + const int32_t Dest = TargetLabel->getPosition() - Buffer.size(); |
| + emitBr(CondMIPS32::AL, nullptr, nullptr, Dest); |
|
Jim Stichnoth
2016/08/19 17:12:20
I think it would be easier to read like this:
sta
|
| + return; |
| + } |
| + const IOffsetT Position = Buffer.size(); |
| + emitBr(CondMIPS32::AL, nullptr, nullptr, TargetLabel->getEncodedPosition()); |
| + TargetLabel->linkTo(*this, Position); |
| +} |
| + |
| +void AssemblerMIPS32::bcc(const CondMIPS32::Cond Cond, const Operand *OpRs, |
| + const Operand *OpRt, Label *TargetLabel) |
| +{ |
| + if (TargetLabel->isBound()) { |
| + const int32_t Dest = TargetLabel->getPosition() - Buffer.size(); |
| + emitBr(Cond, OpRs, OpRt, Dest); |
| + return; |
| + } |
| + const IOffsetT Position = Buffer.size(); |
| + emitBr(Cond, OpRs, OpRt, TargetLabel->getEncodedPosition()); |
| + TargetLabel->linkTo(*this, Position); |
| +} |
| + |
| +void AssemblerMIPS32::bzc(const CondMIPS32::Cond Cond, const Operand *OpRs, |
| + Label *TargetLabel) |
| +{ |
| + if (TargetLabel->isBound()) { |
| + const int32_t Dest = TargetLabel->getPosition() - Buffer.size(); |
| + emitBr(Cond, OpRs, nullptr, Dest); |
| + return; |
| + } |
| + const IOffsetT Position = Buffer.size(); |
| + emitBr(Cond, OpRs, nullptr, TargetLabel->getEncodedPosition()); |
| + TargetLabel->linkTo(*this, Position); |
| +} |
| + |
| +} // end of namespace MIPS32 |
| +} // end of namespace Ice |
| + |
|
Jim Stichnoth
2016/08/19 17:12:21
remove trailing whitespace (blank line) to keep gi
|