1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-02-01 05:01:59 +01:00

[WebAssembly] Add an option to make get_local/set_local explicit.

This patch adds a pass, controlled by an option and off by default for
now, for making implicit get_local/set_local explicit. This simplifies
emitting wasm with MC.

Differential Revision: https://reviews.llvm.org/D25836

llvm-svn: 285009
This commit is contained in:
Dan Gohman 2016-10-24 19:49:43 +00:00
parent 4459b43829
commit a62715a542
22 changed files with 631 additions and 176 deletions

View File

@ -14,6 +14,7 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyAsmPrinter.cpp
WebAssemblyCallIndirectFixup.cpp
WebAssemblyCFGStackify.cpp
WebAssemblyExplicitLocals.cpp
WebAssemblyFastISel.cpp
WebAssemblyFixIrreducibleControlFlow.cpp
WebAssemblyFrameLowering.cpp
@ -40,6 +41,7 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyTargetMachine.cpp
WebAssemblyTargetObjectFile.cpp
WebAssemblyTargetTransformInfo.cpp
WebAssemblyUtilities.cpp
)
add_dependencies(LLVMWebAssemblyCodeGen intrinsics_gen)

View File

@ -93,6 +93,7 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
const MCOperandInfo &Info = Desc.OpInfo[i];
switch (Info.OperandType) {
case MCOI::OPERAND_IMMEDIATE:
case WebAssembly::OPERAND_LOCAL:
case WebAssembly::OPERAND_P2ALIGN:
case WebAssembly::OPERAND_BASIC_BLOCK: {
if (Pos + sizeof(uint64_t) > Bytes.size())

View File

@ -149,16 +149,27 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
if (OpNo < MII.get(MI->getOpcode()).getNumDefs())
O << '=';
} else if (Op.isImm()) {
assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() ||
(MII.get(MI->getOpcode()).TSFlags &
WebAssemblyII::VariableOpIsImmediate)) &&
const MCInstrDesc &Desc = MII.get(MI->getOpcode());
assert((OpNo < Desc.getNumOperands() ||
(Desc.TSFlags & WebAssemblyII::VariableOpIsImmediate)) &&
"WebAssemblyII::VariableOpIsImmediate should be set for "
"variable_ops immediate ops");
// TODO: (MII.get(MI->getOpcode()).TSFlags &
// WebAssemblyII::VariableOpImmediateIsLabel)
// can tell us whether this is an immediate referencing a label in the
// control flow stack, and it may be nice to pretty-print.
O << Op.getImm();
if (Desc.TSFlags & WebAssemblyII::VariableOpImmediateIsType) {
switch (Op.getImm()) {
case int64_t(WebAssembly::ValType::I32): O << "i32"; break;
case int64_t(WebAssembly::ValType::I64): O << "i64"; break;
case int64_t(WebAssembly::ValType::F32): O << "f32"; break;
case int64_t(WebAssembly::ValType::F64): O << "f64"; break;
default: llvm_unreachable("unknown local type");
}
} else {
// TODO: (MII.get(MI->getOpcode()).TSFlags &
// WebAssemblyII::VariableOpImmediateIsLabel)
// can tell us whether this is an immediate referencing a label in the
// control flow stack, and it may be nice to pretty-print.
O << Op.getImm();
}
} else if (Op.isFPImm()) {
const MCInstrDesc &Desc = MII.get(MI->getOpcode());
assert(OpNo < Desc.getNumOperands() &&
@ -200,18 +211,18 @@ WebAssemblyInstPrinter::printWebAssemblySignatureOperand(const MCInst *MI,
unsigned OpNo,
raw_ostream &O) {
int64_t Imm = MI->getOperand(OpNo).getImm();
switch (Imm) {
case WebAssembly::Void: break;
case WebAssembly::I32: O << "i32"; break;
case WebAssembly::I64: O << "i64"; break;
case WebAssembly::F32: O << "f32"; break;
case WebAssembly::F64: O << "f64"; break;
case WebAssembly::I8x16: O << "i8x16"; break;
case WebAssembly::I16x8: O << "i16x8"; break;
case WebAssembly::I32x4: O << "i32x4"; break;
case WebAssembly::I64x2: O << "i32x4"; break;
case WebAssembly::F32x4: O << "f32x4"; break;
case WebAssembly::F64x2: O << "f64x2"; break;
switch (WebAssembly::ExprType(Imm)) {
case WebAssembly::ExprType::Void: break;
case WebAssembly::ExprType::I32: O << "i32"; break;
case WebAssembly::ExprType::I64: O << "i64"; break;
case WebAssembly::ExprType::F32: O << "f32"; break;
case WebAssembly::ExprType::F64: O << "f64"; break;
case WebAssembly::ExprType::I8x16: O << "i8x16"; break;
case WebAssembly::ExprType::I16x8: O << "i16x8"; break;
case WebAssembly::ExprType::I32x4: O << "i32x4"; break;
case WebAssembly::ExprType::I64x2: O << "i32x4"; break;
case WebAssembly::ExprType::F32x4: O << "f32x4"; break;
case WebAssembly::ExprType::F64x2: O << "f64x2"; break;
}
}

View File

@ -23,6 +23,7 @@
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@ -56,30 +57,54 @@ MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) {
void WebAssemblyMCCodeEmitter::encodeInstruction(
const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const {
// FIXME: This is not the real binary encoding. This is an extremely
// over-simplified encoding where we just use uint64_t for everything. This
// is a temporary measure.
support::endian::Writer<support::little>(OS).write<uint64_t>(MI.getOpcode());
uint64_t Start = OS.tell();
uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
encodeULEB128(Binary, OS);
const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
if (Desc.isVariadic())
support::endian::Writer<support::little>(OS).write<uint64_t>(
MI.getNumOperands() - Desc.NumOperands);
for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) {
const MCOperand &MO = MI.getOperand(i);
if (MO.isReg()) {
support::endian::Writer<support::little>(OS).write<uint64_t>(MO.getReg());
/* nothing to encode */
} else if (MO.isImm()) {
support::endian::Writer<support::little>(OS).write<uint64_t>(MO.getImm());
assert(i < Desc.getNumOperands() &&
"Unexpected integer immediate as a non-fixed operand");
assert(Desc.TSFlags == 0 &&
"WebAssembly variable_ops integer ops don't use TSFlags");
const MCOperandInfo &Info = Desc.OpInfo[i];
if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
encodeSLEB128(int32_t(MO.getImm()), OS);
} else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
encodeSLEB128(int64_t(MO.getImm()), OS);
} else {
encodeULEB128(uint64_t(MO.getImm()), OS);
}
} else if (MO.isFPImm()) {
support::endian::Writer<support::little>(OS).write<double>(MO.getFPImm());
assert(i < Desc.getNumOperands() &&
"Unexpected floating-point immediate as a non-fixed operand");
assert(Desc.TSFlags == 0 &&
"WebAssembly variable_ops floating point ops don't use TSFlags");
const MCOperandInfo &Info = Desc.OpInfo[i];
if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
// TODO: MC converts all floating point immediate operands to double.
// This is fine for numeric values, but may cause NaNs to change bits.
float f = float(MO.getFPImm());
support::endian::Writer<support::little>(OS).write<float>(f);
} else {
assert(Info.OperandType == WebAssembly::OPERAND_F64IMM);
double d = MO.getFPImm();
support::endian::Writer<support::little>(OS).write<double>(d);
}
} else if (MO.isExpr()) {
support::endian::Writer<support::little>(OS).write<uint64_t>(0);
Fixups.push_back(MCFixup::create(
(1 + MCII.get(MI.getOpcode()).isVariadic() + i) * sizeof(uint64_t),
MO.getExpr(),
OS.tell() - Start, MO.getExpr(),
STI.getTargetTriple().isArch64Bit() ? FK_Data_8 : FK_Data_4,
MI.getLoc()));
++MCNumFixups;
encodeULEB128(STI.getTargetTriple().isArch64Bit() ? UINT64_MAX
: uint64_t(UINT32_MAX),
OS);
} else {
llvm_unreachable("unexpected operand kind");
}

View File

@ -44,6 +44,8 @@ namespace WebAssembly {
enum OperandType {
/// Basic block label in a branch construct.
OPERAND_BASIC_BLOCK = MCOI::OPERAND_FIRST_TARGET,
/// Local index.
OPERAND_LOCAL,
/// 32-bit integer immediates.
OPERAND_I32IMM,
/// 64-bit integer immediates.
@ -78,6 +80,9 @@ enum {
// For immediate values in the variable_ops range, this flag indicates
// whether the value represents a control-flow label.
VariableOpImmediateIsLabel = (1 << 1),
// For immediate values in the variable_ops range, this flag indicates
// whether the value represents a ValType.
VariableOpImmediateIsType = (1 << 2),
};
} // end namespace WebAssemblyII
@ -144,7 +149,7 @@ static const unsigned LoadP2AlignOperandNo = 3;
static const unsigned StoreP2AlignOperandNo = 2;
/// This is used to indicate block signatures.
enum ExprType {
enum class ExprType {
Void = 0,
I32 = 1,
I64 = 2,
@ -158,6 +163,20 @@ enum ExprType {
F64x2 = 10
};
/// This is used to indicate local types.
enum class ValType {
I32 = 1,
I64 = 2,
F32 = 3,
F64 = 4,
I8x16 = 5,
I16x8 = 6,
I32x4 = 7,
I64x2 = 8,
F32x4 = 9,
F64x2 = 10
};
} // end namespace WebAssembly
} // end namespace llvm

View File

@ -43,6 +43,7 @@ FunctionPass *createWebAssemblyOptimizeLiveIntervals();
FunctionPass *createWebAssemblyStoreResults();
FunctionPass *createWebAssemblyRegStackify();
FunctionPass *createWebAssemblyRegColoring();
FunctionPass *createWebAssemblyExplicitLocals();
FunctionPass *createWebAssemblyFixIrreducibleControlFlow();
FunctionPass *createWebAssemblyCFGStackify();
FunctionPass *createWebAssemblyLowerBrUnless();

View File

@ -30,6 +30,7 @@
#include "WebAssembly.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
@ -63,23 +64,6 @@ FunctionPass *llvm::createWebAssemblyArgumentMove() {
return new WebAssemblyArgumentMove();
}
/// Test whether the given instruction is an ARGUMENT.
static bool IsArgument(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::ARGUMENT_I32:
case WebAssembly::ARGUMENT_I64:
case WebAssembly::ARGUMENT_F32:
case WebAssembly::ARGUMENT_F64:
case WebAssembly::ARGUMENT_v16i8:
case WebAssembly::ARGUMENT_v8i16:
case WebAssembly::ARGUMENT_v4i32:
case WebAssembly::ARGUMENT_v4f32:
return true;
default:
return false;
}
}
bool WebAssemblyArgumentMove::runOnMachineFunction(MachineFunction &MF) {
DEBUG({
dbgs() << "********** Argument Move **********\n"
@ -92,7 +76,7 @@ bool WebAssemblyArgumentMove::runOnMachineFunction(MachineFunction &MF) {
// Look for the first NonArg instruction.
for (MachineInstr &MI : EntryMBB) {
if (!IsArgument(MI)) {
if (!WebAssembly::isArgument(MI)) {
InsertPt = MI;
break;
}
@ -101,7 +85,7 @@ bool WebAssemblyArgumentMove::runOnMachineFunction(MachineFunction &MF) {
// Now move any argument instructions later in the block
// to before our first NonArg instruction.
for (MachineInstr &MI : llvm::make_range(InsertPt, EntryMBB.end())) {
if (IsArgument(MI)) {
if (WebAssembly::isArgument(MI)) {
EntryMBB.insert(InsertPt, MI.removeFromParent());
Changed = true;
}

View File

@ -27,6 +27,7 @@
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/ADT/PriorityQueue.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/CodeGen/MachineDominators.h"
@ -292,19 +293,6 @@ static bool ExplicitlyBranchesTo(MachineBasicBlock *Pred,
return false;
}
/// Test whether MI is a child of some other node in an expression tree.
static bool IsChild(const MachineInstr &MI,
const WebAssemblyFunctionInfo &MFI) {
if (MI.getNumOperands() == 0)
return false;
const MachineOperand &MO = MI.getOperand(0);
if (!MO.isReg() || MO.isImplicit() || !MO.isDef())
return false;
unsigned Reg = MO.getReg();
return TargetRegisterInfo::isVirtualRegister(Reg) &&
MFI.isVRegStackified(Reg);
}
/// Insert a BLOCK marker for branches to MBB (if needed).
static void PlaceBlockMarker(
MachineBasicBlock &MBB, MachineFunction &MF,
@ -365,7 +353,7 @@ static void PlaceBlockMarker(
// beginning of the local expression tree and any nested BLOCKs.
InsertPos = Header->getFirstTerminator();
while (InsertPos != Header->begin() &&
IsChild(*std::prev(InsertPos), MFI) &&
WebAssembly::isChild(*std::prev(InsertPos), MFI) &&
std::prev(InsertPos)->getOpcode() != WebAssembly::LOOP &&
std::prev(InsertPos)->getOpcode() != WebAssembly::END_BLOCK &&
std::prev(InsertPos)->getOpcode() != WebAssembly::END_LOOP)
@ -375,7 +363,7 @@ static void PlaceBlockMarker(
// Add the BLOCK.
MachineInstr *Begin = BuildMI(*Header, InsertPos, DebugLoc(),
TII.get(WebAssembly::BLOCK))
.addImm(WebAssembly::Void);
.addImm(int64_t(WebAssembly::ExprType::Void));
// Mark the end of the block.
InsertPos = MBB.begin();
@ -425,7 +413,7 @@ static void PlaceLoopMarker(
++InsertPos;
MachineInstr *Begin = BuildMI(MBB, InsertPos, DebugLoc(),
TII.get(WebAssembly::LOOP))
.addImm(WebAssembly::Void);
.addImm(int64_t(WebAssembly::ExprType::Void));
// Mark the end of the loop.
MachineInstr *End = BuildMI(*AfterLoop, AfterLoop->begin(), DebugLoc(),
@ -471,16 +459,16 @@ static void FixEndsAtEndOfFunction(
WebAssembly::ExprType retType;
switch (MFI.getResults().front().SimpleTy) {
case MVT::i32: retType = WebAssembly::I32; break;
case MVT::i64: retType = WebAssembly::I64; break;
case MVT::f32: retType = WebAssembly::F32; break;
case MVT::f64: retType = WebAssembly::F64; break;
case MVT::v16i8: retType = WebAssembly::I8x16; break;
case MVT::v8i16: retType = WebAssembly::I16x8; break;
case MVT::v4i32: retType = WebAssembly::I32x4; break;
case MVT::v2i64: retType = WebAssembly::I64x2; break;
case MVT::v4f32: retType = WebAssembly::F32x4; break;
case MVT::v2f64: retType = WebAssembly::F64x2; break;
case MVT::i32: retType = WebAssembly::ExprType::I32; break;
case MVT::i64: retType = WebAssembly::ExprType::I64; break;
case MVT::f32: retType = WebAssembly::ExprType::F32; break;
case MVT::f64: retType = WebAssembly::ExprType::F64; break;
case MVT::v16i8: retType = WebAssembly::ExprType::I8x16; break;
case MVT::v8i16: retType = WebAssembly::ExprType::I16x8; break;
case MVT::v4i32: retType = WebAssembly::ExprType::I32x4; break;
case MVT::v2i64: retType = WebAssembly::ExprType::I64x2; break;
case MVT::v4f32: retType = WebAssembly::ExprType::F32x4; break;
case MVT::v2f64: retType = WebAssembly::ExprType::F64x2; break;
default: llvm_unreachable("unexpected return type");
}

View File

@ -0,0 +1,309 @@
//===-- WebAssemblyExplicitLocals.cpp - Make Locals Explicit --------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file converts any remaining registers into WebAssembly locals.
///
/// After register stackification and register coloring, convert non-stackified
/// registers into locals, inserting explicit get_local and set_local
/// instructions.
///
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssembly.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "wasm-explicit-locals"
namespace {
class WebAssemblyExplicitLocals final : public MachineFunctionPass {
StringRef getPassName() const override {
return "WebAssembly Explicit Locals";
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
AU.addPreserved<MachineBlockFrequencyInfo>();
MachineFunctionPass::getAnalysisUsage(AU);
}
bool runOnMachineFunction(MachineFunction &MF) override;
public:
static char ID; // Pass identification, replacement for typeid
WebAssemblyExplicitLocals() : MachineFunctionPass(ID) {}
};
} // end anonymous namespace
char WebAssemblyExplicitLocals::ID = 0;
FunctionPass *llvm::createWebAssemblyExplicitLocals() {
return new WebAssemblyExplicitLocals();
}
/// Return a local id number for the given register, assigning it a new one
/// if it doesn't yet have one.
static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local,
unsigned &CurLocal, unsigned Reg) {
return Reg2Local.insert(std::make_pair(Reg, CurLocal++)).first->second;
}
/// Get the appropriate get_local opcode for the given register class.
static unsigned getGetLocalOpcode(const TargetRegisterClass *RC) {
if (RC == &WebAssembly::I32RegClass)
return WebAssembly::GET_LOCAL_I32;
if (RC == &WebAssembly::I64RegClass)
return WebAssembly::GET_LOCAL_I64;
if (RC == &WebAssembly::F32RegClass)
return WebAssembly::GET_LOCAL_F32;
if (RC == &WebAssembly::F64RegClass)
return WebAssembly::GET_LOCAL_F64;
if (RC == &WebAssembly::V128RegClass)
return WebAssembly::GET_LOCAL_V128;
llvm_unreachable("Unexpected register class");
}
/// Get the appropriate set_local opcode for the given register class.
static unsigned getSetLocalOpcode(const TargetRegisterClass *RC) {
if (RC == &WebAssembly::I32RegClass)
return WebAssembly::SET_LOCAL_I32;
if (RC == &WebAssembly::I64RegClass)
return WebAssembly::SET_LOCAL_I64;
if (RC == &WebAssembly::F32RegClass)
return WebAssembly::SET_LOCAL_F32;
if (RC == &WebAssembly::F64RegClass)
return WebAssembly::SET_LOCAL_F64;
if (RC == &WebAssembly::V128RegClass)
return WebAssembly::SET_LOCAL_V128;
llvm_unreachable("Unexpected register class");
}
/// Get the appropriate tee_local opcode for the given register class.
static unsigned getTeeLocalOpcode(const TargetRegisterClass *RC) {
if (RC == &WebAssembly::I32RegClass)
return WebAssembly::TEE_LOCAL_I32;
if (RC == &WebAssembly::I64RegClass)
return WebAssembly::TEE_LOCAL_I64;
if (RC == &WebAssembly::F32RegClass)
return WebAssembly::TEE_LOCAL_F32;
if (RC == &WebAssembly::F64RegClass)
return WebAssembly::TEE_LOCAL_F64;
if (RC == &WebAssembly::V128RegClass)
return WebAssembly::TEE_LOCAL_V128;
llvm_unreachable("Unexpected register class");
}
/// Get the type associated with the given register class.
static WebAssembly::ValType typeForRegClass(const TargetRegisterClass *RC) {
if (RC == &WebAssembly::I32RegClass)
return WebAssembly::ValType::I32;
if (RC == &WebAssembly::I64RegClass)
return WebAssembly::ValType::I64;
if (RC == &WebAssembly::F32RegClass)
return WebAssembly::ValType::F32;
if (RC == &WebAssembly::F64RegClass)
return WebAssembly::ValType::F64;
llvm_unreachable("unrecognized register class");
}
/// Given a MachineOperand of a stackified vreg, return the instruction at the
/// start of the expression tree.
static MachineInstr *FindStartOfTree(MachineOperand &MO,
MachineRegisterInfo &MRI,
WebAssemblyFunctionInfo &MFI) {
unsigned Reg = MO.getReg();
assert(MFI.isVRegStackified(Reg));
MachineInstr *Def = MRI.getVRegDef(Reg);
// Find the first stackified use and proceed from there.
for (MachineOperand &DefMO : Def->explicit_uses()) {
if (!DefMO.isReg())
continue;
return FindStartOfTree(DefMO, MRI, MFI);
}
// If there were no stackified uses, we've reached the start.
return Def;
}
bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
DEBUG(dbgs() << "********** Make Locals Explicit **********\n"
"********** Function: "
<< MF.getName() << '\n');
bool Changed = false;
MachineRegisterInfo &MRI = MF.getRegInfo();
WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
// Map non-stackified virtual registers to their local ids.
DenseMap<unsigned, unsigned> Reg2Local;
// Handle ARGUMENTS first to ensure that they get the designated numbers.
for (MachineBasicBlock::iterator I = MF.begin()->begin(),
E = MF.begin()->end();
I != E;) {
MachineInstr &MI = *I++;
if (!WebAssembly::isArgument(MI))
break;
unsigned Reg = MI.getOperand(0).getReg();
assert(!MFI.isVRegStackified(Reg));
Reg2Local[Reg] = MI.getOperand(1).getImm();
MI.eraseFromParent();
Changed = true;
}
// Start assigning local numbers after the last parameter.
unsigned CurLocal = MFI.getParams().size();
// Visit each instruction in the function.
for (MachineBasicBlock &MBB : MF) {
for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) {
MachineInstr &MI = *I++;
assert(!WebAssembly::isArgument(MI));
if (MI.isDebugValue() || MI.isLabel())
continue;
// Replace tee instructions with tee_local. The difference is that tee
// instructins have two defs, while tee_local instructions have one def
// and an index of a local to write to.
if (WebAssembly::isTee(MI)) {
assert(MFI.isVRegStackified(MI.getOperand(0).getReg()));
assert(!MFI.isVRegStackified(MI.getOperand(1).getReg()));
unsigned OldReg = MI.getOperand(2).getReg();
const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
// Stackify the input if it isn't stackified yet.
if (!MFI.isVRegStackified(OldReg)) {
unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
unsigned NewReg = MRI.createVirtualRegister(RC);
unsigned Opc = getGetLocalOpcode(RC);
BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg)
.addImm(LocalId);
MI.getOperand(2).setReg(NewReg);
MFI.stackifyVReg(NewReg);
}
// Replace the TEE with a TEE_LOCAL.
unsigned LocalId =
getLocalId(Reg2Local, CurLocal, MI.getOperand(1).getReg());
unsigned Opc = getTeeLocalOpcode(RC);
BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc),
MI.getOperand(0).getReg())
.addImm(LocalId)
.addReg(MI.getOperand(2).getReg());
MI.eraseFromParent();
Changed = true;
continue;
}
// Insert set_locals for any defs that aren't stackified yet. Currently
// we handle at most one def.
assert(MI.getDesc().getNumDefs() <= 1);
if (MI.getDesc().getNumDefs() == 1) {
unsigned OldReg = MI.getOperand(0).getReg();
if (!MFI.isVRegStackified(OldReg) && !MRI.use_empty(OldReg)) {
unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
unsigned NewReg = MRI.createVirtualRegister(RC);
auto InsertPt = std::next(MachineBasicBlock::iterator(&MI));
unsigned Opc = getSetLocalOpcode(RC);
BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
.addImm(LocalId)
.addReg(NewReg);
MI.getOperand(0).setReg(NewReg);
MFI.stackifyVReg(NewReg);
Changed = true;
}
}
// Insert get_locals for any uses that aren't stackified yet.
MachineInstr *InsertPt = &MI;
for (MachineOperand &MO : reverse(MI.explicit_uses())) {
if (!MO.isReg())
continue;
unsigned OldReg = MO.getReg();
// If we see a stackified register, prepare to insert subsequent
// get_locals before the start of its tree.
if (MFI.isVRegStackified(OldReg)) {
InsertPt = FindStartOfTree(MO, MRI, MFI);
continue;
}
// Insert a get_local.
unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
unsigned NewReg = MRI.createVirtualRegister(RC);
unsigned Opc = getGetLocalOpcode(RC);
InsertPt =
BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc), NewReg)
.addImm(LocalId);
MO.setReg(NewReg);
MFI.stackifyVReg(NewReg);
Changed = true;
}
// Coalesce and eliminate COPY instructions.
if (WebAssembly::isCopy(MI)) {
MRI.replaceRegWith(MI.getOperand(1).getReg(),
MI.getOperand(0).getReg());
MI.eraseFromParent();
Changed = true;
}
}
}
// Insert a .locals directive to declare the locals.
MachineInstrBuilder DeclareLocals;
for (size_t i = 0, e = MRI.getNumVirtRegs(); i < e; ++i) {
unsigned Reg = TargetRegisterInfo::index2VirtReg(i);
auto I = Reg2Local.find(Reg);
if (I == Reg2Local.end() || I->second < MFI.getParams().size())
continue;
if (!DeclareLocals) {
DeclareLocals = BuildMI(*MF.begin(), MF.begin()->begin(), DebugLoc(),
TII->get(WebAssembly::DECLARE_LOCALS));
Changed = true;
}
DeclareLocals.addImm(int64_t(typeForRegClass(MRI.getRegClass(Reg))));
}
#ifndef NDEBUG
// Assert that all registers have been stackified at this point.
for (const MachineBasicBlock &MBB : MF) {
for (const MachineInstr &MI : MBB) {
if (MI.isDebugValue() || MI.isLabel())
continue;
for (const MachineOperand &MO : MI.explicit_operands()) {
assert(
(!MO.isReg() || MRI.use_empty(MO.getReg()) ||
MFI.isVRegStackified(MO.getReg())) &&
"WebAssemblyExplicitLocals failed to stackify a register operand");
}
}
}
#endif
return Changed;
}

View File

@ -542,8 +542,8 @@ unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) {
&WebAssembly::I64RegClass :
&WebAssembly::I32RegClass);
unsigned Opc = Subtarget->hasAddr64() ?
WebAssembly::COPY_LOCAL_I64 :
WebAssembly::COPY_LOCAL_I32;
WebAssembly::COPY_I64 :
WebAssembly::COPY_I32;
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg)
.addFrameIndex(SI->second);
return ResultReg;

View File

@ -587,8 +587,8 @@ SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op,
unsigned Reg = cast<RegisterSDNode>(Op.getOperand(1))->getReg();
EVT VT = Src.getValueType();
SDValue Copy(
DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_LOCAL_I32
: WebAssembly::COPY_LOCAL_I64,
DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_I32
: WebAssembly::COPY_I64,
DL, VT, Src),
0);
return Op.getNode()->getNumValues() == 1

View File

@ -60,19 +60,19 @@ void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
? MRI.getRegClass(DestReg)
: MRI.getTargetRegisterInfo()->getMinimalPhysRegClass(DestReg);
unsigned CopyLocalOpcode;
unsigned CopyOpcode;
if (RC == &WebAssembly::I32RegClass)
CopyLocalOpcode = WebAssembly::COPY_LOCAL_I32;
CopyOpcode = WebAssembly::COPY_I32;
else if (RC == &WebAssembly::I64RegClass)
CopyLocalOpcode = WebAssembly::COPY_LOCAL_I64;
CopyOpcode = WebAssembly::COPY_I64;
else if (RC == &WebAssembly::F32RegClass)
CopyLocalOpcode = WebAssembly::COPY_LOCAL_F32;
CopyOpcode = WebAssembly::COPY_F32;
else if (RC == &WebAssembly::F64RegClass)
CopyLocalOpcode = WebAssembly::COPY_LOCAL_F64;
CopyOpcode = WebAssembly::COPY_F64;
else
llvm_unreachable("Unexpected register class");
BuildMI(MBB, I, DL, get(CopyLocalOpcode), DestReg)
BuildMI(MBB, I, DL, get(CopyOpcode), DestReg)
.addReg(SrcReg, KillSrc ? RegState::Kill : 0);
}

View File

@ -71,6 +71,9 @@ let OperandNamespace = "WebAssembly" in {
let OperandType = "OPERAND_BASIC_BLOCK" in
def bb_op : Operand<OtherVT>;
let OperandType = "OPERAND_LOCAL" in
def local_op : Operand<i32>;
let OperandType = "OPERAND_I32IMM" in
def i32imm_op : Operand<i32>;
@ -133,20 +136,42 @@ let Defs = [ARGUMENTS] in {
// are implied by virtual register uses and defs.
multiclass LOCAL<WebAssemblyRegClass vt> {
let hasSideEffects = 0 in {
// COPY_LOCAL is not an actual instruction in wasm, but since we allow
// get_local and set_local to be implicit, we can have a COPY_LOCAL which
// is actually a no-op because all the work is done in the implied
// get_local and set_local.
let isAsCheapAsAMove = 1 in
def COPY_LOCAL_#vt : I<(outs vt:$res), (ins vt:$src), [],
"copy_local\t$res, $src">;
// COPY is not an actual instruction in wasm, but since we allow get_local and
// set_local to be implicit during most of codegen, we can have a COPY which
// is actually a no-op because all the work is done in the implied get_local
// and set_local. COPYs are eliminated (and replaced with
// get_local/set_local) in the ExplicitLocals pass.
let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in
def COPY_#vt : I<(outs vt:$res), (ins vt:$src), [], "copy_local\t$res, $src">;
// TEE is similar to COPY, but writes two copies of its result. Typically
// this would be used to stackify one result and write the other result to a
// local.
let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in
def TEE_#vt : I<(outs vt:$res, vt:$also), (ins vt:$src), [],
"tee_local\t$res, $also, $src">;
// This is the actual get_local instruction in wasm. These are made explicit
// by the ExplicitLocals pass. It has mayLoad because it reads from a wasm
// local, which is a side effect not otherwise modeled in LLVM.
let mayLoad = 1, isAsCheapAsAMove = 1 in
def GET_LOCAL_#vt : I<(outs vt:$res), (ins local_op:$local), [],
"get_local\t$res, $local", 0x14>;
// This is the actual set_local instruction in wasm. These are made explicit
// by the ExplicitLocals pass. It has mayStore because it writes to a wasm
// local, which is a side effect not otherwise modeled in LLVM.
let mayStore = 1, isAsCheapAsAMove = 1 in
def SET_LOCAL_#vt : I<(outs), (ins local_op:$local, vt:$src), [],
"set_local\t$local, $src", 0x15>;
// This is the actual tee_local instruction in wasm. TEEs are turned into
// TEE_LOCALs by the ExplicitLocals pass. It has mayStore for the same reason
// as SET_LOCAL.
let mayStore = 1, isAsCheapAsAMove = 1 in
def TEE_LOCAL_#vt : I<(outs vt:$res), (ins local_op:$local, vt:$src), [],
"tee_local\t$res, $local, $src", 0x19>;
// TEE_LOCAL is similar to COPY_LOCAL, but writes two copies of its result.
// Typically this would be used to stackify one result and write the other
// result to a local.
let isAsCheapAsAMove = 1 in
def TEE_LOCAL_#vt : I<(outs vt:$res, vt:$also), (ins vt:$src), [],
"tee_local\t$res, $also, $src">;
} // hasSideEffects = 0
}
defm : LOCAL<I32>;
@ -155,19 +180,26 @@ defm : LOCAL<F32>;
defm : LOCAL<F64>;
defm : LOCAL<V128>, Requires<[HasSIMD128]>;
// Set TSFlags{0} to 1 to indicate that the variable_ops are immediates.
// Set TSFlags{2} to 1 to indicate that the immediates are ValTypes.
def DECLARE_LOCALS : I<(outs), (ins variable_ops), [], ".local \t"> {
let TSFlags{0} = 1;
let TSFlags{2} = 1;
}
let isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 in {
def CONST_I32 : I<(outs I32:$res), (ins i32imm_op:$imm),
[(set I32:$res, imm:$imm)],
"i32.const\t$res, $imm">;
"i32.const\t$res, $imm", 0x10>;
def CONST_I64 : I<(outs I64:$res), (ins i64imm_op:$imm),
[(set I64:$res, imm:$imm)],
"i64.const\t$res, $imm">;
"i64.const\t$res, $imm", 0x11>;
def CONST_F32 : I<(outs F32:$res), (ins f32imm_op:$imm),
[(set F32:$res, fpimm:$imm)],
"f32.const\t$res, $imm">;
"f32.const\t$res, $imm", 0x13>;
def CONST_F64 : I<(outs F64:$res), (ins f64imm_op:$imm),
[(set F64:$res, fpimm:$imm)],
"f64.const\t$res, $imm">;
"f64.const\t$res, $imm", 0x12>;
} // isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1
} // Defs = [ARGUMENTS]

View File

@ -107,9 +107,9 @@ bool WebAssemblyLowerBrUnless::runOnMachineFunction(MachineFunction &MF) {
// instruction to invert it.
if (!Inverted) {
unsigned Tmp = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
MFI.stackifyVReg(Tmp);
BuildMI(MBB, MI, MI->getDebugLoc(), TII.get(WebAssembly::EQZ_I32), Tmp)
.addReg(Cond);
MFI.stackifyVReg(Tmp);
Cond = Tmp;
Inverted = true;
}

View File

@ -64,6 +64,7 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo {
static const unsigned UnusedReg = -1u;
void stackifyVReg(unsigned VReg) {
assert(MF.getRegInfo().getUniqueVRegDef(VReg));
if (TargetRegisterInfo::virtReg2Index(VReg) >= VRegStackified.size())
VRegStackified.resize(TargetRegisterInfo::virtReg2Index(VReg) + 1);
VRegStackified.set(TargetRegisterInfo::virtReg2Index(VReg));

View File

@ -83,8 +83,8 @@ static bool MaybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB,
if (&MI != &MBB.back())
return false;
// If the operand isn't stackified, insert a COPY_LOCAL to read the operand
// and stackify it.
// If the operand isn't stackified, insert a COPY to read the operand and
// stackify it.
MachineOperand &MO = MI.getOperand(0);
unsigned Reg = MO.getReg();
if (!MFI.isVRegStackified(Reg)) {
@ -150,42 +150,42 @@ bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
case WebAssembly::RETURN_I32:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I32,
WebAssembly::COPY_LOCAL_I32);
WebAssembly::COPY_I32);
break;
case WebAssembly::RETURN_I64:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I64,
WebAssembly::COPY_LOCAL_I64);
WebAssembly::COPY_I64);
break;
case WebAssembly::RETURN_F32:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F32,
WebAssembly::COPY_LOCAL_F32);
WebAssembly::COPY_F32);
break;
case WebAssembly::RETURN_F64:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F64,
WebAssembly::COPY_LOCAL_F64);
WebAssembly::COPY_F64);
break;
case WebAssembly::RETURN_v16i8:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v16i8,
WebAssembly::COPY_LOCAL_V128);
WebAssembly::COPY_V128);
break;
case WebAssembly::RETURN_v8i16:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v8i16,
WebAssembly::COPY_LOCAL_V128);
WebAssembly::COPY_V128);
break;
case WebAssembly::RETURN_v4i32:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4i32,
WebAssembly::COPY_LOCAL_V128);
WebAssembly::COPY_V128);
break;
case WebAssembly::RETURN_v4f32:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4f32,
WebAssembly::COPY_LOCAL_V128);
WebAssembly::COPY_V128);
break;
case WebAssembly::RETURN_VOID:
if (!DisableWebAssemblyFallthroughReturnOpt &&

View File

@ -23,6 +23,7 @@
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
@ -58,27 +59,10 @@ FunctionPass *llvm::createWebAssemblyPrepareForLiveIntervals() {
return new WebAssemblyPrepareForLiveIntervals();
}
/// Test whether the given instruction is an ARGUMENT.
static bool IsArgument(const MachineInstr *MI) {
switch (MI->getOpcode()) {
case WebAssembly::ARGUMENT_I32:
case WebAssembly::ARGUMENT_I64:
case WebAssembly::ARGUMENT_F32:
case WebAssembly::ARGUMENT_F64:
case WebAssembly::ARGUMENT_v16i8:
case WebAssembly::ARGUMENT_v8i16:
case WebAssembly::ARGUMENT_v4i32:
case WebAssembly::ARGUMENT_v4f32:
return true;
default:
return false;
}
}
// Test whether the given register has an ARGUMENT def.
static bool HasArgumentDef(unsigned Reg, const MachineRegisterInfo &MRI) {
for (const auto &Def : MRI.def_instructions(Reg))
if (IsArgument(&Def))
if (WebAssembly::isArgument(Def))
return true;
return false;
}
@ -126,10 +110,10 @@ bool WebAssemblyPrepareForLiveIntervals::runOnMachineFunction(MachineFunction &M
// Move ARGUMENT_* instructions to the top of the entry block, so that their
// liveness reflects the fact that these really are live-in values.
for (auto MII = Entry.begin(), MIE = Entry.end(); MII != MIE; ) {
MachineInstr *MI = &*MII++;
if (IsArgument(MI)) {
MI->removeFromParent();
Entry.insert(Entry.begin(), MI);
MachineInstr &MI = *MII++;
if (WebAssembly::isArgument(MI)) {
MI.removeFromParent();
Entry.insert(Entry.begin(), &MI);
}
}

View File

@ -17,6 +17,7 @@
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
@ -68,24 +69,13 @@ bool WebAssemblyRegNumbering::runOnMachineFunction(MachineFunction &MF) {
// variables. Assign the numbers for them first.
MachineBasicBlock &EntryMBB = MF.front();
for (MachineInstr &MI : EntryMBB) {
switch (MI.getOpcode()) {
case WebAssembly::ARGUMENT_I32:
case WebAssembly::ARGUMENT_I64:
case WebAssembly::ARGUMENT_F32:
case WebAssembly::ARGUMENT_F64:
case WebAssembly::ARGUMENT_v16i8:
case WebAssembly::ARGUMENT_v8i16:
case WebAssembly::ARGUMENT_v4i32:
case WebAssembly::ARGUMENT_v4f32: {
int64_t Imm = MI.getOperand(1).getImm();
DEBUG(dbgs() << "Arg VReg " << MI.getOperand(0).getReg() << " -> WAReg "
<< Imm << "\n");
MFI.setWAReg(MI.getOperand(0).getReg(), Imm);
if (!WebAssembly::isArgument(MI))
break;
}
default:
break;
}
int64_t Imm = MI.getOperand(1).getImm();
DEBUG(dbgs() << "Arg VReg " << MI.getOperand(0).getReg() << " -> WAReg "
<< Imm << "\n");
MFI.setWAReg(MI.getOperand(0).getReg(), Imm);
}
// Then assign regular WebAssembly registers for all remaining used

View File

@ -24,6 +24,7 @@
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" // for WebAssembly::ARGUMENT_*
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/CodeGen/LiveIntervalAnalysis.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
@ -404,18 +405,18 @@ static bool OneUseDominatesOtherUses(unsigned Reg, const MachineOperand &OneUse,
return true;
}
/// Get the appropriate tee_local opcode for the given register class.
static unsigned GetTeeLocalOpcode(const TargetRegisterClass *RC) {
/// Get the appropriate tee opcode for the given register class.
static unsigned GetTeeOpcode(const TargetRegisterClass *RC) {
if (RC == &WebAssembly::I32RegClass)
return WebAssembly::TEE_LOCAL_I32;
return WebAssembly::TEE_I32;
if (RC == &WebAssembly::I64RegClass)
return WebAssembly::TEE_LOCAL_I64;
return WebAssembly::TEE_I64;
if (RC == &WebAssembly::F32RegClass)
return WebAssembly::TEE_LOCAL_F32;
return WebAssembly::TEE_F32;
if (RC == &WebAssembly::F64RegClass)
return WebAssembly::TEE_LOCAL_F64;
return WebAssembly::TEE_F64;
if (RC == &WebAssembly::V128RegClass)
return WebAssembly::TEE_LOCAL_V128;
return WebAssembly::TEE_V128;
llvm_unreachable("Unexpected register class");
}
@ -513,8 +514,8 @@ static MachineInstr *RematerializeCheapDef(
/// A multiple-use def in the same block with no intervening memory or register
/// dependencies; move the def down, nest it with the current instruction, and
/// insert a tee_local to satisfy the rest of the uses. As an illustration,
/// rewrite this:
/// insert a tee to satisfy the rest of the uses. As an illustration, rewrite
/// this:
///
/// Reg = INST ... // Def
/// INST ..., Reg, ... // Insert
@ -524,7 +525,7 @@ static MachineInstr *RematerializeCheapDef(
/// to this:
///
/// DefReg = INST ... // Def (to become the new Insert)
/// TeeReg, Reg = TEE_LOCAL_... DefReg
/// TeeReg, Reg = TEE_... DefReg
/// INST ..., TeeReg, ... // Insert
/// INST ..., Reg, ...
/// INST ..., Reg, ...
@ -547,7 +548,7 @@ static MachineInstr *MoveAndTeeForMultiUse(
unsigned DefReg = MRI.createVirtualRegister(RegClass);
MachineOperand &DefMO = Def->getOperand(0);
MachineInstr *Tee = BuildMI(MBB, Insert, Insert->getDebugLoc(),
TII->get(GetTeeLocalOpcode(RegClass)), TeeReg)
TII->get(GetTeeOpcode(RegClass)), TeeReg)
.addReg(Reg, RegState::Define)
.addReg(DefReg, getUndefRegState(DefMO.isDead()));
Op.setReg(TeeReg);
@ -759,18 +760,11 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
// Argument instructions represent live-in registers and not real
// instructions.
if (Def->getOpcode() == WebAssembly::ARGUMENT_I32 ||
Def->getOpcode() == WebAssembly::ARGUMENT_I64 ||
Def->getOpcode() == WebAssembly::ARGUMENT_F32 ||
Def->getOpcode() == WebAssembly::ARGUMENT_F64 ||
Def->getOpcode() == WebAssembly::ARGUMENT_v16i8 ||
Def->getOpcode() == WebAssembly::ARGUMENT_v8i16 ||
Def->getOpcode() == WebAssembly::ARGUMENT_v4i32 ||
Def->getOpcode() == WebAssembly::ARGUMENT_v4f32)
if (WebAssembly::isArgument(*Def))
continue;
// Decide which strategy to take. Prefer to move a single-use value
// over cloning it, and prefer cloning over introducing a tee_local.
// over cloning it, and prefer cloning over introducing a tee.
// For moving, we require the def to be in the same block as the use;
// this makes things simpler (LiveIntervals' handleMove function only
// supports intra-block moves) and it's MachineSink's job to catch all

View File

@ -41,6 +41,11 @@ static cl::opt<bool> EnableEmSjLj(
cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"),
cl::init(false));
static cl::opt<bool> ExplicitLocals(
"wasm-explicit-locals",
cl::desc("WebAssembly with explicit get_local/set_local"),
cl::init(false));
extern "C" void LLVMInitializeWebAssemblyTarget() {
// Register the target.
RegisterTargetMachine<WebAssemblyTargetMachine> X(
@ -256,6 +261,10 @@ void WebAssemblyPassConfig::addPreEmitPass() {
addPass(createWebAssemblyRegColoring());
}
// Insert explicit get_local and set_local operators.
if (ExplicitLocals)
addPass(createWebAssemblyExplicitLocals());
// Eliminate multiple-entry loops.
addPass(createWebAssemblyFixIrreducibleControlFlow());

View File

@ -0,0 +1,71 @@
//===-- WebAssemblyUtilities.cpp - WebAssembly Utility Functions ----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file implements several utility functions for WebAssembly.
///
//===----------------------------------------------------------------------===//
#include "WebAssemblyUtilities.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "llvm/CodeGen/MachineInstr.h"
using namespace llvm;
bool WebAssembly::isArgument(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::ARGUMENT_I32:
case WebAssembly::ARGUMENT_I64:
case WebAssembly::ARGUMENT_F32:
case WebAssembly::ARGUMENT_F64:
case WebAssembly::ARGUMENT_v16i8:
case WebAssembly::ARGUMENT_v8i16:
case WebAssembly::ARGUMENT_v4i32:
case WebAssembly::ARGUMENT_v4f32:
return true;
default:
return false;
}
}
bool WebAssembly::isCopy(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::COPY_I32:
case WebAssembly::COPY_I64:
case WebAssembly::COPY_F32:
case WebAssembly::COPY_F64:
return true;
default:
return false;
}
}
bool WebAssembly::isTee(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::TEE_I32:
case WebAssembly::TEE_I64:
case WebAssembly::TEE_F32:
case WebAssembly::TEE_F64:
return true;
default:
return false;
}
}
/// Test whether MI is a child of some other node in an expression tree.
bool WebAssembly::isChild(const MachineInstr &MI,
const WebAssemblyFunctionInfo &MFI) {
if (MI.getNumOperands() == 0)
return false;
const MachineOperand &MO = MI.getOperand(0);
if (!MO.isReg() || MO.isImplicit() || !MO.isDef())
return false;
unsigned Reg = MO.getReg();
return TargetRegisterInfo::isVirtualRegister(Reg) &&
MFI.isVRegStackified(Reg);
}

View File

@ -0,0 +1,34 @@
//===-- WebAssemblyUtilities - WebAssembly Utility Functions ---*- C++ -*-====//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file contains the declaration of the WebAssembly-specific
/// utility functions.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYUTILITIES_H
#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYUTILITIES_H
namespace llvm {
class MachineInstr;
class WebAssemblyFunctionInfo;
namespace WebAssembly {
bool isArgument(const MachineInstr &MI);
bool isCopy(const MachineInstr &MI);
bool isTee(const MachineInstr &MI);
bool isChild(const MachineInstr &MI, const WebAssemblyFunctionInfo &MFI);
} // end namespace WebAssembly
} // end namespace llvm
#endif