Skip to content

Commit

Permalink
[WebAssemby] Implement block signatures.
Browse files Browse the repository at this point in the history
Per spec changes, this implements block signatures, and adds just enough
logic to produce correct block signatures at the ends of functions.

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

llvm-svn: 283503
  • Loading branch information
Dan Gohman committed Oct 6, 2016
1 parent 3a643e8 commit 2726b88
Show file tree
Hide file tree
Showing 15 changed files with 317 additions and 177 deletions.
20 changes: 20 additions & 0 deletions llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,26 @@ WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst *MI,
O << ":p2align=" << Imm;
}

void
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;
}
}

const char *llvm::WebAssembly::TypeToString(MVT Ty) {
switch (Ty.SimpleTy) {
case MVT::i32:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class WebAssemblyInstPrinter final : public MCInstPrinter {
void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O);
void printWebAssemblyP2AlignOperand(const MCInst *MI, unsigned OpNo,
raw_ostream &O);
void printWebAssemblySignatureOperand(const MCInst *MI, unsigned OpNo,
raw_ostream &O);

// Autogenerated by tblgen.
void printInstruction(const MCInst *MI, raw_ostream &O);
Expand Down
21 changes: 19 additions & 2 deletions llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ enum OperandType {
/// 64-bit floating-point immediates.
OPERAND_F64IMM,
/// p2align immediate for load and store address alignment.
OPERAND_P2ALIGN
OPERAND_P2ALIGN,
/// signature immediate for block/loop.
OPERAND_SIGNATURE
};

/// WebAssembly-specific directive identifiers.
Expand All @@ -63,7 +65,7 @@ enum Directive {
DotResult = UINT64_MAX - 1, ///< .result
DotLocal = UINT64_MAX - 2, ///< .local
DotEndFunc = UINT64_MAX - 3, ///< .endfunc
DotIndIdx = UINT64_MAX - 4, /// < .indidx
DotIndIdx = UINT64_MAX - 4, ///< .indidx
};

} // end namespace WebAssembly
Expand Down Expand Up @@ -141,6 +143,21 @@ static const unsigned StoreAddressOperandNo = 1;
static const unsigned LoadP2AlignOperandNo = 3;
static const unsigned StoreP2AlignOperandNo = 2;

/// This is used to indicate block signatures.
enum ExprType {
Void = 0,
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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,17 @@ void WebAssemblyTargetAsmStreamer::emitLocal(ArrayRef<MVT> Types) {
void WebAssemblyTargetAsmStreamer::emitEndFunc() { OS << "\t.endfunc\n"; }

void WebAssemblyTargetAsmStreamer::emitIndirectFunctionType(
StringRef name, SmallVectorImpl<MVT> &SignatureVTs, size_t NumResults) {
StringRef name, SmallVectorImpl<MVT> &Params, SmallVectorImpl<MVT> &Results) {
OS << "\t.functype\t" << name;
if (NumResults == 0)
if (Results.empty())
OS << ", void";
for (auto Ty : SignatureVTs) {
OS << ", " << WebAssembly::TypeToString(Ty);
else {
assert(Results.size() == 1);
OS << ", " << WebAssembly::TypeToString(Results.front());
}
OS << "\n";
for (auto Ty : Params)
OS << ", " << WebAssembly::TypeToString(Ty);
OS << '\n';
}

void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class WebAssemblyTargetStreamer : public MCTargetStreamer {
virtual void emitEndFunc() = 0;
/// .functype
virtual void emitIndirectFunctionType(StringRef name,
SmallVectorImpl<MVT> &SignatureVTs,
size_t NumResults) {
SmallVectorImpl<MVT> &Params,
SmallVectorImpl<MVT> &Results) {
llvm_unreachable("emitIndirectFunctionType not implemented");
}
/// .indidx
Expand All @@ -59,8 +59,8 @@ class WebAssemblyTargetAsmStreamer final : public WebAssemblyTargetStreamer {
void emitLocal(ArrayRef<MVT> Types) override;
void emitEndFunc() override;
void emitIndirectFunctionType(StringRef name,
SmallVectorImpl<MVT> &SignatureVTs,
size_t NumResults) override;
SmallVectorImpl<MVT> &Params,
SmallVectorImpl<MVT> &Results) override;
void emitIndIdx(const MCExpr *Value) override;
};

Expand Down
39 changes: 5 additions & 34 deletions llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,45 +121,16 @@ WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() {
//===----------------------------------------------------------------------===//
// WebAssemblyAsmPrinter Implementation.
//===----------------------------------------------------------------------===//
static void ComputeLegalValueVTs(const Function &F, const TargetMachine &TM,
Type *Ty, SmallVectorImpl<MVT> &ValueVTs) {
const DataLayout &DL(F.getParent()->getDataLayout());
const WebAssemblyTargetLowering &TLI =
*TM.getSubtarget<WebAssemblySubtarget>(F).getTargetLowering();
SmallVector<EVT, 4> VTs;
ComputeValueVTs(TLI, DL, Ty, VTs);

for (EVT VT : VTs) {
unsigned NumRegs = TLI.getNumRegisters(F.getContext(), VT);
MVT RegisterVT = TLI.getRegisterType(F.getContext(), VT);
for (unsigned i = 0; i != NumRegs; ++i)
ValueVTs.push_back(RegisterVT);
}
}

void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
for (const auto &F : M) {
// Emit function type info for all undefined functions
if (F.isDeclarationForLinker() && !F.isIntrinsic()) {
SmallVector<MVT, 4> SignatureVTs;
ComputeLegalValueVTs(F, TM, F.getReturnType(), SignatureVTs);
size_t NumResults = SignatureVTs.size();
if (SignatureVTs.size() > 1) {
// WebAssembly currently can't lower returns of multiple values without
// demoting to sret (see WebAssemblyTargetLowering::CanLowerReturn). So
// replace multiple return values with a pointer parameter.
SignatureVTs.clear();
SignatureVTs.push_back(
MVT::getIntegerVT(M.getDataLayout().getPointerSizeInBits()));
NumResults = 0;
}

for (auto &Arg : F.args()) {
ComputeLegalValueVTs(F, TM, Arg.getType(), SignatureVTs);
}

getTargetStreamer()->emitIndirectFunctionType(F.getName(), SignatureVTs,
NumResults);
SmallVector<MVT, 4> Results;
SmallVector<MVT, 4> Params;
ComputeSignatureVTs(F, TM, Params, Results);
getTargetStreamer()->emitIndirectFunctionType(F.getName(), Params,
Results);
}
}
}
Expand Down
86 changes: 75 additions & 11 deletions llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ static bool IsChild(const MachineInstr &MI,
static void PlaceBlockMarker(
MachineBasicBlock &MBB, MachineFunction &MF,
SmallVectorImpl<MachineBasicBlock *> &ScopeTops,
DenseMap<const MachineInstr *, const MachineBasicBlock *> &LoopTops,
DenseMap<const MachineInstr *, MachineInstr *> &BlockTops,
DenseMap<const MachineInstr *, MachineInstr *> &LoopTops,
const WebAssemblyInstrInfo &TII,
const MachineLoopInfo &MLI,
MachineDominatorTree &MDT,
Expand Down Expand Up @@ -372,15 +373,19 @@ static void PlaceBlockMarker(
}

// Add the BLOCK.
BuildMI(*Header, InsertPos, DebugLoc(), TII.get(WebAssembly::BLOCK));
MachineInstr *Begin = BuildMI(*Header, InsertPos, DebugLoc(),
TII.get(WebAssembly::BLOCK))
.addImm(WebAssembly::Void);

// Mark the end of the block.
InsertPos = MBB.begin();
while (InsertPos != MBB.end() &&
InsertPos->getOpcode() == WebAssembly::END_LOOP &&
LoopTops[&*InsertPos]->getNumber() >= Header->getNumber())
LoopTops[&*InsertPos]->getParent()->getNumber() >= Header->getNumber())
++InsertPos;
BuildMI(MBB, InsertPos, DebugLoc(), TII.get(WebAssembly::END_BLOCK));
MachineInstr *End = BuildMI(MBB, InsertPos, DebugLoc(),
TII.get(WebAssembly::END_BLOCK));
BlockTops[End] = Begin;

// Track the farthest-spanning scope that ends at this point.
int Number = MBB.getNumber();
Expand All @@ -393,7 +398,7 @@ static void PlaceBlockMarker(
static void PlaceLoopMarker(
MachineBasicBlock &MBB, MachineFunction &MF,
SmallVectorImpl<MachineBasicBlock *> &ScopeTops,
DenseMap<const MachineInstr *, const MachineBasicBlock *> &LoopTops,
DenseMap<const MachineInstr *, MachineInstr *> &LoopTops,
const WebAssemblyInstrInfo &TII, const MachineLoopInfo &MLI) {
MachineLoop *Loop = MLI.getLoopFor(&MBB);
if (!Loop || Loop->getHeader() != &MBB)
Expand All @@ -418,12 +423,14 @@ static void PlaceLoopMarker(
while (InsertPos != MBB.end() &&
InsertPos->getOpcode() == WebAssembly::END_LOOP)
++InsertPos;
BuildMI(MBB, InsertPos, DebugLoc(), TII.get(WebAssembly::LOOP));
MachineInstr *Begin = BuildMI(MBB, InsertPos, DebugLoc(),
TII.get(WebAssembly::LOOP))
.addImm(WebAssembly::Void);

// Mark the end of the loop.
MachineInstr *End = BuildMI(*AfterLoop, AfterLoop->begin(), DebugLoc(),
TII.get(WebAssembly::END_LOOP));
LoopTops[End] = &MBB;
LoopTops[End] = Begin;

assert((!ScopeTops[AfterLoop->getNumber()] ||
ScopeTops[AfterLoop->getNumber()]->getNumber() < MBB.getNumber()) &&
Expand All @@ -445,6 +452,56 @@ GetDepth(const SmallVectorImpl<const MachineBasicBlock *> &Stack,
return Depth;
}

/// In normal assembly languages, when the end of a function is unreachable,
/// because the function ends in an infinite loop or a noreturn call or similar,
/// it isn't necessary to worry about the function return type at the end of
/// the function, because it's never reached. However, in WebAssembly, blocks
/// that end at the function end need to have a return type signature that
/// matches the function signature, even though it's unreachable. This function
/// checks for such cases and fixes up the signatures.
static void FixEndsAtEndOfFunction(
MachineFunction &MF,
const WebAssemblyFunctionInfo &MFI,
DenseMap<const MachineInstr *, MachineInstr *> &BlockTops,
DenseMap<const MachineInstr *, MachineInstr *> &LoopTops) {
assert(MFI.getResults().size() <= 1);

if (MFI.getResults().empty())
return;

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;
default: llvm_unreachable("unexpected return type");
}

for (MachineBasicBlock &MBB : reverse(MF)) {
for (MachineInstr &MI : reverse(MBB)) {
if (MI.isPosition() || MI.isDebugValue())
continue;
if (MI.getOpcode() == WebAssembly::END_BLOCK) {
BlockTops[&MI]->getOperand(0).setImm(int32_t(retType));
continue;
}
if (MI.getOpcode() == WebAssembly::END_LOOP) {
LoopTops[&MI]->getOperand(0).setImm(int32_t(retType));
continue;
}
// Something other than an `end`. We're done.
return;
}
}
}

/// Insert LOOP and BLOCK markers at appropriate places.
static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI,
const WebAssemblyInstrInfo &TII,
Expand All @@ -457,15 +514,18 @@ static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI,
// we may insert at the end.
SmallVector<MachineBasicBlock *, 8> ScopeTops(MF.getNumBlockIDs() + 1);

// For eacn LOOP_END, the corresponding LOOP.
DenseMap<const MachineInstr *, const MachineBasicBlock *> LoopTops;
// For each LOOP_END, the corresponding LOOP.
DenseMap<const MachineInstr *, MachineInstr *> LoopTops;

// For each END_BLOCK, the corresponding BLOCK.
DenseMap<const MachineInstr *, MachineInstr *> BlockTops;

for (auto &MBB : MF) {
// Place the LOOP for MBB if MBB is the header of a loop.
PlaceLoopMarker(MBB, MF, ScopeTops, LoopTops, TII, MLI);

// Place the BLOCK for MBB if MBB is branched to from above.
PlaceBlockMarker(MBB, MF, ScopeTops, LoopTops, TII, MLI, MDT, MFI);
PlaceBlockMarker(MBB, MF, ScopeTops, BlockTops, LoopTops, TII, MLI, MDT, MFI);
}

// Now rewrite references to basic blocks to be depth immediates.
Expand All @@ -486,7 +546,7 @@ static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI,
Stack.push_back(&MBB);
break;
case WebAssembly::END_LOOP:
Stack.push_back(LoopTops[&MI]);
Stack.push_back(LoopTops[&MI]->getParent());
break;
default:
if (MI.isTerminator()) {
Expand All @@ -505,6 +565,10 @@ static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI,
}
}
assert(Stack.empty() && "Control flow should be balanced");

// Fix up block/loop signatures at the end of the function to conform to
// WebAssembly's rules.
FixEndsAtEndOfFunction(MF, MFI, BlockTops, LoopTops);
}

bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) {
Expand Down
13 changes: 10 additions & 3 deletions llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,12 +481,12 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments(
SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL,
SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
MachineFunction &MF = DAG.getMachineFunction();
auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>();

if (!CallingConvSupported(CallConv))
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");

MachineFunction &MF = DAG.getMachineFunction();
auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>();

// Set up the incoming ARGUMENTS value, which serves to represent the liveness
// of the incoming values before they're represented by virtual registers.
MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS);
Expand Down Expand Up @@ -526,6 +526,13 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments(
MFI->addParam(PtrVT);
}

// Record the number and types of results.
SmallVector<MVT, 4> Params;
SmallVector<MVT, 4> Results;
ComputeSignatureVTs(*MF.getFunction(), DAG.getTarget(), Params, Results);
for (MVT VT : Results)
MFI->addResult(VT);

return Chain;
}

Expand Down
4 changes: 2 additions & 2 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ def BR_TABLE_I64 : I<(outs), (ins I64:$index, variable_ops),
// use/clobber VALUE_STACK to prevent them from being moved into the middle of
// an expression tree.
let Uses = [VALUE_STACK], Defs = [VALUE_STACK] in {
def BLOCK : I<(outs), (ins), [], "block", 0x01>;
def LOOP : I<(outs), (ins), [], "loop", 0x02>;
def BLOCK : I<(outs), (ins Signature:$sig), [], "block \t$sig", 0x01>;
def LOOP : I<(outs), (ins Signature:$sig), [], "loop \t$sig", 0x02>;
def END_BLOCK : I<(outs), (ins), [], "end_block">;
def END_LOOP : I<(outs), (ins), [], "end_loop">;
} // Uses = [VALUE_STACK], Defs = [VALUE_STACK]
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ def P2Align : Operand<i32> {
}
} // OperandType = "OPERAND_P2ALIGN"

let OperandType = "OPERAND_SIGNATURE" in {
def Signature : Operand<i32> {
let PrintMethod = "printWebAssemblySignatureOperand";
}
} // OperandType = "OPERAND_SIGNATURE"

} // OperandNamespace = "WebAssembly"

//===----------------------------------------------------------------------===//
Expand Down
Loading

0 comments on commit 2726b88

Please sign in to comment.