Skip to content

Commit d3b9fd0

Browse files
[WebAssembly] Implement addrspacecast to funcref (llvm#166820)
Adds lowering of `addrspacecast [0 -> 20]` to allow easy conversion of function pointers to Wasm `funcref` When given a constant function pointer, it lowers to a direct `ref.func`. Otherwise it lowers to a `table.get` from `__indirect_function_table` using the provided pointer as the index.
1 parent 8f9ef4b commit d3b9fd0

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,10 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
411411
setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom);
412412
setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom);
413413

414+
// Allow converting function ptrs in address space 0 to Wasm funcref (address
415+
// space 20)
416+
setOperationAction(ISD::ADDRSPACECAST, MVT::funcref, Custom);
417+
414418
setMaxAtomicSizeInBitsSupported(64);
415419

416420
// Always convert switches to br_tables unless there is only one case, which
@@ -1756,6 +1760,8 @@ SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op,
17561760
return LowerMUL_LOHI(Op, DAG);
17571761
case ISD::UADDO:
17581762
return LowerUADDO(Op, DAG);
1763+
case ISD::ADDRSPACECAST:
1764+
return LowerADDRSPACECAST(Op, DAG);
17591765
}
17601766
}
17611767

@@ -1899,6 +1905,58 @@ SDValue WebAssemblyTargetLowering::LowerUADDO(SDValue Op,
18991905
return DAG.getMergeValues(Ops, DL);
19001906
}
19011907

1908+
SDValue WebAssemblyTargetLowering::LowerADDRSPACECAST(SDValue Op,
1909+
SelectionDAG &DAG) const {
1910+
SDLoc DL(Op);
1911+
1912+
AddrSpaceCastSDNode *ACN = cast<AddrSpaceCastSDNode>(Op.getNode());
1913+
1914+
if (ACN->getSrcAddressSpace() !=
1915+
WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_DEFAULT ||
1916+
ACN->getDestAddressSpace() !=
1917+
WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF)
1918+
return SDValue();
1919+
1920+
if (ACN->getValueType(0) != MVT::funcref) {
1921+
reportFatalInternalError("Cannot addrspacecast to funcref addrspace with "
1922+
"results other than MVT::funcref");
1923+
}
1924+
1925+
SDValue Src = ACN->getOperand(0);
1926+
1927+
// Lower addrspacecasts of direct/constant function ptrs to ref.func
1928+
if (auto *GA = dyn_cast<GlobalAddressSDNode>(
1929+
Src->getOpcode() == WebAssemblyISD::Wrapper ? Src->getOperand(0)
1930+
: Src)) {
1931+
auto *GV = GA->getGlobal();
1932+
1933+
if (const Function *F = dyn_cast<Function>(GV)) {
1934+
SDValue FnAddress = DAG.getTargetGlobalAddress(F, DL, MVT::i32);
1935+
1936+
SDValue RefFuncNode =
1937+
DAG.getNode(WebAssemblyISD::REF_FUNC, DL, MVT::funcref, FnAddress);
1938+
return RefFuncNode;
1939+
}
1940+
}
1941+
1942+
// Lower everything else to a table.get from the indirect function table
1943+
const MachineFunction &MF = DAG.getMachineFunction();
1944+
1945+
MVT PtrVT = getPointerTy(MF.getDataLayout());
1946+
1947+
MCSymbolWasm *Table =
1948+
WebAssembly::getOrCreateFunctionTableSymbol(MF.getContext(), Subtarget);
1949+
SDValue TableSym = DAG.getMCSymbol(Table, PtrVT);
1950+
1951+
SDValue TableSlot = Op.getOperand(0);
1952+
1953+
SDValue Result(DAG.getMachineNode(WebAssembly::TABLE_GET_FUNCREF, DL,
1954+
MVT::funcref, TableSym, TableSlot),
1955+
0);
1956+
1957+
return Result;
1958+
}
1959+
19021960
SDValue WebAssemblyTargetLowering::Replace128Op(SDNode *N,
19031961
SelectionDAG &DAG) const {
19041962
assert(Subtarget->hasWideArithmetic());

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ class WebAssemblyTargetLowering final : public TargetLowering {
121121
SDValue LowerMUL_LOHI(SDValue Op, SelectionDAG &DAG) const;
122122
SDValue Replace128Op(SDNode *N, SelectionDAG &DAG) const;
123123
SDValue LowerUADDO(SDValue Op, SelectionDAG &DAG) const;
124+
SDValue LowerADDRSPACECAST(SDValue Op, SelectionDAG &DAG) const;
124125

125126
// Custom DAG combine hooks
126127
SDValue

llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
///
1212
//===----------------------------------------------------------------------===//
1313

14+
def WebAssemblyRefFunc_t : SDTypeProfile<1, 1, [SDTCisVT<0, funcref>, SDTCisPtrTy<1>]>;
15+
def WebAssemblyRefFunc :
16+
SDNode<"WebAssemblyISD::REF_FUNC", WebAssemblyRefFunc_t,
17+
[]>;
18+
1419
multiclass REF_I<WebAssemblyRegClass rc, ValueType vt, string ht> {
1520
defm REF_NULL_#rc : I<(outs rc:$dst), (ins),
1621
(outs), (ins),
@@ -42,7 +47,7 @@ defm REF_TEST_FUNCREF : I<(outs I32:$res), (ins TypeIndex:$type, FUNCREF:$ref),
4247
Requires<[HasGC]>;
4348

4449
defm REF_FUNC : I<(outs FUNCREF:$res), (ins function32_op:$func),
45-
(outs), (ins function32_op:$func), [],
50+
(outs), (ins function32_op:$func), [(set FUNCREF:$res, (WebAssemblyRefFunc tglobaladdr:$func))],
4651
"ref.func\t$func", "ref.func $func", 0xd2>,
4752
Requires<[HasReferenceTypes]>;
4853

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
2+
; RUN: llc -mtriple=wasm32-unknown-unknown -mattr=+reference-types < %s | FileCheck -check-prefixes=CHECK,WASM32 %s
3+
; RUN: llc -mtriple=wasm64-unknown-unknown -mattr=+reference-types < %s | FileCheck -check-prefixes=CHECK,WASM64 %s
4+
5+
%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral
6+
7+
declare void @foo();
8+
9+
@global_var = local_unnamed_addr global i32 0
10+
11+
define %funcref @cast_const_funcptr() {
12+
; CHECK-LABEL: cast_const_funcptr:
13+
; CHECK: .functype cast_const_funcptr () -> (funcref)
14+
; CHECK-NEXT: # %bb.0:
15+
; CHECK-NEXT: ref.func foo
16+
; CHECK-NEXT: # fallthrough-return
17+
%result = addrspacecast ptr @foo to %funcref
18+
ret %funcref %result
19+
}
20+
21+
define %funcref @cast_const_not_funcptr() {
22+
; WASM32-LABEL: cast_const_not_funcptr:
23+
; WASM32: .functype cast_const_not_funcptr () -> (funcref)
24+
; WASM32-NEXT: # %bb.0:
25+
; WASM32-NEXT: i32.const global_var
26+
; WASM32-NEXT: table.get __indirect_function_table
27+
; WASM32-NEXT: # fallthrough-return
28+
;
29+
; WASM64-LABEL: cast_const_not_funcptr:
30+
; WASM64: .functype cast_const_not_funcptr () -> (funcref)
31+
; WASM64-NEXT: # %bb.0:
32+
; WASM64-NEXT: i64.const global_var
33+
; WASM64-NEXT: table.get __indirect_function_table
34+
; WASM64-NEXT: # fallthrough-return
35+
%result = addrspacecast ptr @global_var to %funcref
36+
ret %funcref %result
37+
}
38+
39+
define %funcref @cast_param_funcptr(ptr %funcptr) {
40+
; WASM32-LABEL: cast_param_funcptr:
41+
; WASM32: .functype cast_param_funcptr (i32) -> (funcref)
42+
; WASM32-NEXT: # %bb.0:
43+
; WASM32-NEXT: local.get 0
44+
; WASM32-NEXT: table.get __indirect_function_table
45+
; WASM32-NEXT: # fallthrough-return
46+
;
47+
; WASM64-LABEL: cast_param_funcptr:
48+
; WASM64: .functype cast_param_funcptr (i64) -> (funcref)
49+
; WASM64-NEXT: # %bb.0:
50+
; WASM64-NEXT: local.get 0
51+
; WASM64-NEXT: table.get __indirect_function_table
52+
; WASM64-NEXT: # fallthrough-return
53+
%result = addrspacecast ptr %funcptr to %funcref
54+
ret %funcref %result
55+
}

0 commit comments

Comments
 (0)