Changeset View
Changeset View
Standalone View
Standalone View
llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
Show First 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | WebAssemblyTargetLowering::WebAssemblyTargetLowering( | ||||
if (Subtarget->hasUnimplementedSIMD128()) { | if (Subtarget->hasUnimplementedSIMD128()) { | ||||
addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass); | addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass); | ||||
addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass); | addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass); | ||||
} | } | ||||
// Compute derived properties from the register classes. | // Compute derived properties from the register classes. | ||||
computeRegisterProperties(Subtarget->getRegisterInfo()); | computeRegisterProperties(Subtarget->getRegisterInfo()); | ||||
setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); | setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); | ||||
setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom); | |||||
setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); | setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); | ||||
setOperationAction(ISD::JumpTable, MVTPtr, Custom); | setOperationAction(ISD::JumpTable, MVTPtr, Custom); | ||||
setOperationAction(ISD::BlockAddress, MVTPtr, Custom); | setOperationAction(ISD::BlockAddress, MVTPtr, Custom); | ||||
setOperationAction(ISD::BRIND, MVT::Other, Custom); | setOperationAction(ISD::BRIND, MVT::Other, Custom); | ||||
// Take the default expansion for va_arg, va_copy, and va_end. There is no | // Take the default expansion for va_arg, va_copy, and va_end. There is no | ||||
// default action for va_start, so we do that custom. | // default action for va_start, so we do that custom. | ||||
setOperationAction(ISD::VASTART, MVT::Other, Custom); | setOperationAction(ISD::VASTART, MVT::Other, Custom); | ||||
▲ Show 20 Lines • Show All 852 Lines • ▼ Show 20 Lines | SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op, | ||||
switch (Op.getOpcode()) { | switch (Op.getOpcode()) { | ||||
default: | default: | ||||
llvm_unreachable("unimplemented operation lowering"); | llvm_unreachable("unimplemented operation lowering"); | ||||
return SDValue(); | return SDValue(); | ||||
case ISD::FrameIndex: | case ISD::FrameIndex: | ||||
return LowerFrameIndex(Op, DAG); | return LowerFrameIndex(Op, DAG); | ||||
case ISD::GlobalAddress: | case ISD::GlobalAddress: | ||||
return LowerGlobalAddress(Op, DAG); | return LowerGlobalAddress(Op, DAG); | ||||
case ISD::GlobalTLSAddress: | |||||
return LowerGlobalTLSAddress(Op, DAG); | |||||
case ISD::ExternalSymbol: | case ISD::ExternalSymbol: | ||||
return LowerExternalSymbol(Op, DAG); | return LowerExternalSymbol(Op, DAG); | ||||
case ISD::JumpTable: | case ISD::JumpTable: | ||||
return LowerJumpTable(Op, DAG); | return LowerJumpTable(Op, DAG); | ||||
case ISD::BR_JT: | case ISD::BR_JT: | ||||
return LowerBR_JT(Op, DAG); | return LowerBR_JT(Op, DAG); | ||||
case ISD::VASTART: | case ISD::VASTART: | ||||
return LowerVASTART(Op, DAG); | return LowerVASTART(Op, DAG); | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, | ||||
} | } | ||||
return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, | return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, | ||||
DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, | DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, | ||||
GA->getOffset(), OperandFlags)); | GA->getOffset(), OperandFlags)); | ||||
} | } | ||||
SDValue | SDValue | ||||
WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op, | |||||
SelectionDAG &DAG) const { | |||||
aheejin: If you do the conversion in `WebAssemblyISelDAGToDAG.cpp`, you don't need to create… | |||||
It seems most other architectures do it in *TargetLowering, so I decided to do the same. quantum: It seems most other architectures do it in `*TargetLowering`, so I decided to do the same. | |||||
Not Done ReplyInline ActionsIt would be good to try @aheejin's suggestion out and see if it leads to cleaner code. Cargo culting other targets is a good way to get started, but we should still try to make our code as simple as possible. tlively: It would be good to try @aheejin's suggestion out and see if it leads to cleaner code. Cargo… | |||||
SDLoc DL(Op); | |||||
EVT VT = Op.getValueType(); | |||||
const auto *GA = cast<GlobalAddressSDNode>(Op); | |||||
assert(!Subtarget->hasAddr64() && "Should define and use GLOBAL_GET_I64"); | |||||
if (GA->getGlobal()->getThreadLocalMode() != GlobalValue::LocalExecTLSModel) { | |||||
fail(DL, DAG, "Only -ftls-model=local-exec is supported for now"); | |||||
return SDValue(); | |||||
} | |||||
SDValue TLSBase = | |||||
DAG.getNode(WebAssemblyISD::GLOBAL_GET, DL, MVT::i32, | |||||
DAG.getTargetExternalSymbol("__tls_base", MVT::i32)); | |||||
Does this offset work when there are non-thread-local globals too? aheejin: Does this offset work when there are non-thread-local globals too? | |||||
Yes, the linker handles the offsets. All thread locals are grouped together by the linker. quantum: Yes, the linker handles the offsets. All thread locals are grouped together by the linker. | |||||
SDValue TLSOffset = DAG.getNode( | |||||
It looks like this supports local-exec, but would need to be extended to handle initial-exec or the other TLS models. Assuming this is correct, could you add a check for the TLS model and report_fatal_error on unsupported models? sunfish: It looks like this supports local-exec, but would need to be extended to handle initial-exec or… | |||||
I'll add an error for non-local-exec for now. quantum: I'll add an error for non-local-exec for now. | |||||
WebAssemblyISD::CONST, DL, VT, | |||||
DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset(), 0)); | |||||
return DAG.getNode(ISD::ADD, DL, VT, TLSBase, TLSOffset); | |||||
} | |||||
SDValue | |||||
WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op, | WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op, | ||||
SelectionDAG &DAG) const { | SelectionDAG &DAG) const { | ||||
SDLoc DL(Op); | SDLoc DL(Op); | ||||
const auto *ES = cast<ExternalSymbolSDNode>(Op); | const auto *ES = cast<ExternalSymbolSDNode>(Op); | ||||
EVT VT = Op.getValueType(); | EVT VT = Op.getValueType(); | ||||
assert(ES->getTargetFlags() == 0 && | assert(ES->getTargetFlags() == 0 && | ||||
"Unexpected target flags on generic ExternalSymbolSDNode"); | "Unexpected target flags on generic ExternalSymbolSDNode"); | ||||
return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, | return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, | ||||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | case Intrinsic::wasm_throw: { | ||||
return DAG.getNode(WebAssemblyISD::THROW, DL, | return DAG.getNode(WebAssemblyISD::THROW, DL, | ||||
MVT::Other, // outchain type | MVT::Other, // outchain type | ||||
{ | { | ||||
Op.getOperand(0), // inchain | Op.getOperand(0), // inchain | ||||
SymNode, // exception symbol | SymNode, // exception symbol | ||||
Op.getOperand(3) // thrown value | Op.getOperand(3) // thrown value | ||||
}); | }); | ||||
} | } | ||||
case Intrinsic::wasm_tls_size: { | |||||
if (Subtarget->hasAddr64()) { | |||||
fail(DL, DAG, "__builtin_wasm_tls_size is not yet supported on wasm64"); | |||||
return SDValue(); | |||||
} | |||||
return DAG.getNode(WebAssemblyISD::GLOBAL_GET, DL, MVT::i32, | |||||
DAG.getTargetExternalSymbol("__tls_size", MVT::i32)); | |||||
} | |||||
} | } | ||||
} | } | ||||
SDValue | SDValue | ||||
WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op, | WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op, | ||||
SelectionDAG &DAG) const { | SelectionDAG &DAG) const { | ||||
SDLoc DL(Op); | SDLoc DL(Op); | ||||
// If sign extension operations are disabled, allow sext_inreg only if operand | // If sign extension operations are disabled, allow sext_inreg only if operand | ||||
▲ Show 20 Lines • Show All 247 Lines • Show Last 20 Lines |
If you do the conversion in WebAssemblyISelDAGToDAG.cpp, you don't need to create WebAssemblyISD node, SDTypeProfile, and SDNode for every single instruction you want to generate. This WebAssemblyISelLowering.cpp is a part of legalization so it cannot generate machine instructions directly, whereas WebAssemblyISelDAGToDAG.cpp is a part of instruction selection so you have direct access to machine instructions. I think moving routines there can be cleaner?