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:
parent
4459b43829
commit
a62715a542
@ -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)
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -43,6 +43,7 @@ FunctionPass *createWebAssemblyOptimizeLiveIntervals();
|
||||
FunctionPass *createWebAssemblyStoreResults();
|
||||
FunctionPass *createWebAssemblyRegStackify();
|
||||
FunctionPass *createWebAssemblyRegColoring();
|
||||
FunctionPass *createWebAssemblyExplicitLocals();
|
||||
FunctionPass *createWebAssemblyFixIrreducibleControlFlow();
|
||||
FunctionPass *createWebAssemblyCFGStackify();
|
||||
FunctionPass *createWebAssemblyLowerBrUnless();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
309
lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
Normal file
309
lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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 &&
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
||||
|
71
lib/Target/WebAssembly/WebAssemblyUtilities.cpp
Normal file
71
lib/Target/WebAssembly/WebAssemblyUtilities.cpp
Normal 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);
|
||||
}
|
34
lib/Target/WebAssembly/WebAssemblyUtilities.h
Normal file
34
lib/Target/WebAssembly/WebAssemblyUtilities.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user