Skip to content

[WebAssembly] Add support for nonnull_extern_ref type #148935

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1675,6 +1675,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// Return a WebAssembly externref type.
QualType getWebAssemblyExternrefType() const;

/// Return a WebAssembly non null externref type.
QualType getWebAssemblyNonNullExternrefType() const;

/// Return the unique reference to a vector type of the specified
/// element type and size.
///
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,9 @@ class QualType {
/// Returns true if it is a WebAssembly Externref Type.
bool isWebAssemblyExternrefType() const;

/// Returns true if it is a WebAssembly non null Externref Type.
bool isWebAssemblyNonNullExternrefType() const;

/// Returns true if it is a WebAssembly Funcref Type.
bool isWebAssemblyFuncrefType() const;

Expand Down Expand Up @@ -2402,6 +2405,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
/// Check if this is a WebAssembly Externref Type.
bool isWebAssemblyExternrefType() const;

/// Check if this is a WebAssembly non null Externref Type.
bool isWebAssemblyNonNullExternrefType() const;

/// Returns true if this is a WebAssembly table type: either an array of
/// reference types, or a pointer to a reference type (which can only be
/// created by array to pointer decay).
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/WebAssemblyReferenceTypes.def
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,7 @@

WASM_REF_TYPE("__externref_t", "externref_t", WasmExternRef, WasmExternRefTy, 10)

WASM_REF_TYPE("__non_null_externref_t", "non_null_externref_t", WasmNonNullExternRef, WasmNonNullExternRefTy, 11)

#undef WASM_TYPE
#undef WASM_REF_TYPE
4 changes: 3 additions & 1 deletion clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3550,6 +3550,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
#define AMDGPU_TYPE(Name, Id, SingletonId, Width, Align) case BuiltinType::Id:
#include "clang/Basic/AMDGPUTypes.def"
case BuiltinType::WasmExternRef:
case BuiltinType::WasmNonNullExternRef:
#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/RISCVVTypes.def"
llvm_unreachable("not yet implemented");
Expand Down Expand Up @@ -4584,7 +4585,8 @@ ASTContext::getBuiltinVectorTypeInfo(const BuiltinType *Ty) const {
QualType ASTContext::getWebAssemblyExternrefType() const {
if (Target->getTriple().isWasm() && Target->hasFeature("reference-types")) {
#define WASM_REF_TYPE(Name, MangledName, Id, SingletonId, AS) \
if (BuiltinType::Id == BuiltinType::WasmExternRef) \
if (BuiltinType::Id == BuiltinType::WasmExternRef || \
BuiltinType::Id == BuiltinType::WasmNonNullExternRef) \
return SingletonId;
#include "clang/Basic/WebAssemblyReferenceTypes.def"
}
Expand Down
13 changes: 12 additions & 1 deletion clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2553,6 +2553,12 @@ bool Type::isWebAssemblyExternrefType() const {
return false;
}

bool Type::isWebAssemblyNonNullExternrefType() const {
if (const auto *BT = getAs<BuiltinType>())
return BT->getKind() == BuiltinType::WasmNonNullExternRef;
return false;
}

bool Type::isWebAssemblyTableType() const {
if (const auto *ATy = dyn_cast<ArrayType>(this))
return ATy->getElementType().isWebAssemblyReferenceType();
Expand Down Expand Up @@ -2922,13 +2928,18 @@ bool QualType::hasNonTrivialToPrimitiveCopyCUnion(const RecordDecl *RD) {
}

bool QualType::isWebAssemblyReferenceType() const {
return isWebAssemblyExternrefType() || isWebAssemblyFuncrefType();
return isWebAssemblyExternrefType() || isWebAssemblyNonNullExternrefType() ||
isWebAssemblyFuncrefType();
}

bool QualType::isWebAssemblyExternrefType() const {
return getTypePtr()->isWebAssemblyExternrefType();
}

bool QualType::isWebAssemblyNonNullExternrefType() const {
return getTypePtr()->isWebAssemblyNonNullExternrefType();
}

bool QualType::isWebAssemblyFuncrefType() const {
return getTypePtr()->isFunctionPointerType() &&
getAddressSpace() == LangAS::wasm_funcref;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,9 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
case BuiltinType::Id: { \
if (BuiltinType::Id == BuiltinType::WasmExternRef) \
ResultType = CGM.getTargetCodeGenInfo().getWasmExternrefReferenceType(); \
else if (BuiltinType::Id == BuiltinType::WasmNonNullExternRef) \
ResultType = \
CGM.getTargetCodeGenInfo().getWasmNonNullExternrefReferenceType(); \
else \
llvm_unreachable("Unexpected wasm reference builtin type!"); \
} break;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,10 @@ class TargetCodeGenInfo {
/// Return the WebAssembly externref reference type.
virtual llvm::Type *getWasmExternrefReferenceType() const { return nullptr; }

/// Return the WebAssembly externref reference type.
virtual llvm::Type *getWasmNonNullExternrefReferenceType() const {
return nullptr;
}
/// Return the WebAssembly funcref reference type.
virtual llvm::Type *getWasmFuncrefReferenceType() const { return nullptr; }

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/Targets/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo {
virtual llvm::Type *getWasmExternrefReferenceType() const override {
return llvm::Type::getWasm_ExternrefTy(getABIInfo().getVMContext());
}
virtual llvm::Type *getWasmNonNullExternrefReferenceType() const override {
return llvm::Type::getWasm_NonNullExternrefTy(getABIInfo().getVMContext());
}
/// Return the WebAssembly funcref reference type.
virtual llvm::Type *getWasmFuncrefReferenceType() const override {
return llvm::Type::getWasm_FuncrefTy(getABIInfo().getVMContext());
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13643,6 +13643,12 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
return;
}

if (VDecl->getType().isWebAssemblyExternrefType() &&
Init->getType()->isWebAssemblyNonNullExternrefType()) {
VDecl->setInit(Init);
return;
}

// C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for.
if (VDecl->getType()->isUndeducedType()) {
if (Init->containsErrors()) {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9638,6 +9638,12 @@ AssignConvertType Sema::CheckAssignmentConstraints(QualType LHSType,
return AssignConvertType::Compatible;
}

if (LHSType->isWebAssemblyExternrefType() &&
RHSType->isWebAssemblyNonNullExternrefType()) {
Kind = CK_NoOp;
return AssignConvertType::Compatible;
}

return AssignConvertType::Incompatible;
}

Expand Down
47 changes: 47 additions & 0 deletions clang/test/CodeGen/WebAssembly/wasm-externref.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +reference-types -o - -emit-llvm %s | FileCheck %s

typedef __externref_t externref_t;
typedef __non_null_externref_t nn_externref_t;

void helper(externref_t);
void helper_2(nn_externref_t);

// CHECK-LABEL: @handle(
// CHECK-NEXT: entry:
Expand All @@ -16,3 +18,48 @@ void helper(externref_t);
void handle(externref_t obj) {
helper(obj);
}


// CHECK-LABEL: @handle_2(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[OBJ_ADDR:%.*]] = alloca ptr addrspace(11), align 1
// CHECK-NEXT: store ptr addrspace(11) [[OBJ:%.*]], ptr [[OBJ_ADDR]], align 1
// CHECK-NEXT: [[TMP0:%.*]] = load ptr addrspace(11), ptr [[OBJ_ADDR]], align 1
// CHECK-NEXT: call void @helper_2(ptr addrspace(11) [[TMP0]])
// CHECK-NEXT: ret void
//
void handle_2(nn_externref_t obj) {
helper_2(obj);
}


nn_externref_t socketpair_js_concat(nn_externref_t, nn_externref_t)
__attribute__((import_module("wasm:js-string"), import_name("concat")));

nn_externref_t get_string_ref(const char *s);
void print_string_ref(nn_externref_t);

// CHECK-LABEL: @socketpair_example(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[STR1:%.*]] = alloca ptr addrspace(11), align 1
// CHECK-NEXT: [[STR2:%.*]] = alloca ptr addrspace(11), align 1
// CHECK-NEXT: [[RESULT:%.*]] = alloca ptr addrspace(11), align 1
// CHECK-NEXT: [[CALL:%.*]] = call ptr addrspace(11) @get_string_ref(ptr noundef @.str)
// CHECK-NEXT: store ptr addrspace(11) [[CALL]], ptr [[STR1]], align 1
// CHECK-NEXT: [[CALL1:%.*]] = call ptr addrspace(11) @get_string_ref(ptr noundef @.str.1)
// CHECK-NEXT: store ptr addrspace(11) [[CALL1]], ptr [[STR2]], align 1
// CHECK-NEXT: [[TMP0:%.*]] = load ptr addrspace(11), ptr [[STR1]], align 1
// CHECK-NEXT: [[TMP1:%.*]] = load ptr addrspace(11), ptr [[STR2]], align 1
// CHECK-NEXT: [[CALL2:%.*]] = call ptr addrspace(11) @socketpair_js_concat(ptr addrspace(11) [[TMP0]], ptr addrspace(11) [[TMP1]])
// CHECK-NEXT: store ptr addrspace(11) [[CALL2]], ptr [[RESULT]], align 1
// CHECK-NEXT: [[TMP2:%.*]] = load ptr addrspace(11), ptr [[RESULT]], align 1
// CHECK-NEXT: call void @print_string_ref(ptr addrspace(11) [[TMP2]])
// CHECK-NEXT: ret void
//
void socketpair_example() {
nn_externref_t str1 = get_string_ref("Hello, ");
nn_externref_t str2 = get_string_ref("world!");
nn_externref_t result = socketpair_js_concat(str1, str2);
print_string_ref(result);
}

63 changes: 63 additions & 0 deletions clang/test/Sema/wasm-refs-and-tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ __externref_t r1;
extern __externref_t r2;
static __externref_t r3;

__non_null_externref_t nn_r1;
extern __non_null_externref_t nn_r2;
static __non_null_externref_t nn_r3;

__externref_t *t1; // expected-error {{pointer to WebAssembly reference type is not allowed}}
__externref_t **t2; // expected-error {{pointer to WebAssembly reference type is not allowed}}
__externref_t ******t3; // expected-error {{pointer to WebAssembly reference type is not allowed}}
Expand All @@ -19,10 +23,24 @@ __externref_t t7[0]; // expected-error {{WebAssembly table must be s
static __externref_t t8[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
static __externref_t (*t9)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}

__non_null_externref_t *nn_t1; // expected-error {{pointer to WebAssembly reference type is not allowed}}
__non_null_externref_t **nn_t2; // expected-error {{pointer to WebAssembly reference type is not allowed}}
__non_null_externref_t ******nn_t3; // expected-error {{pointer to WebAssembly reference type is not allowed}}
static __non_null_externref_t nn_t4[3]; // expected-error {{only zero-length WebAssembly tables are currently supported}}
static __non_null_externref_t nn_t5[]; // expected-error {{only zero-length WebAssembly tables are currently supported}}
static __non_null_externref_t nn_t6[] = {0}; // expected-error {{only zero-length WebAssembly tables are currently supported}}
__non_null_externref_t nn_t7[0]; // expected-error {{WebAssembly table must be static}}
static __non_null_externref_t nn_t8[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
static __non_null_externref_t (*nn_t9)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}

static __externref_t table[0];
static __externref_t other_table[0] = {};
static __externref_t another_table[] = {}; // expected-error {{only zero-length WebAssembly tables are currently supported}}

static __non_null_externref_t nn_table[0];
static __non_null_externref_t nn_other_table[0] = {};
static __non_null_externref_t nn_another_table[] = {}; // expected-error {{only zero-length WebAssembly tables are currently supported}}

struct s {
__externref_t f1; // expected-error {{field has sizeless type '__externref_t'}}
__externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}}
Expand All @@ -33,6 +51,17 @@ struct s {
__externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
};


struct nn_s {
__non_null_externref_t nn_f1; // expected-error {{field has sizeless type '__non_null_externref_t'}}
__non_null_externref_t nn_f2[0]; // expected-error {{field has sizeless type '__non_null_externref_t'}}
__non_null_externref_t nn_f3[]; // expected-error {{field has sizeless type '__non_null_externref_t'}}
__non_null_externref_t nn_f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
__non_null_externref_t *nn_f5; // expected-error {{pointer to WebAssembly reference type is not allowed}}
__non_null_externref_t ****nn_f6; // expected-error {{pointer to WebAssembly reference type is not allowed}}
__non_null_externref_t (*nn_f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
};

union u {
__externref_t f1; // expected-error {{field has sizeless type '__externref_t'}}
__externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}}
Expand All @@ -43,16 +72,38 @@ union u {
__externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
};


union nn_u {
__non_null_externref_t nn_f1; // expected-error {{field has sizeless type '__non_null_externref_t'}}
__non_null_externref_t nn_f2[0]; // expected-error {{field has sizeless type '__non_null_externref_t'}}
__non_null_externref_t nn_f3[]; // expected-error {{field has sizeless type '__non_null_externref_t'}}
__non_null_externref_t nn_f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
__non_null_externref_t *nn_f5; // expected-error {{pointer to WebAssembly reference type is not allowed}}
__non_null_externref_t ****nn_f6; // expected-error {{pointer to WebAssembly reference type is not allowed}}
__non_null_externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
};

void illegal_argument_1(__externref_t table[]); // expected-error {{cannot use WebAssembly table as a function parameter}}
void illegal_argument_2(__externref_t table[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
void illegal_argument_3(__externref_t *table); // expected-error {{pointer to WebAssembly reference type is not allowed}}
void illegal_argument_4(__externref_t ***table); // expected-error {{pointer to WebAssembly reference type is not allowed}}
void illegal_argument_5(__externref_t (*table)[0]); // expected-error {{cannot form a pointer to a WebAssembly table}}
void illegal_argument_6(__externref_t table[0]); // expected-error {{cannot use WebAssembly table as a function parameter}}

void illegal_nn_argument_1(__non_null_externref_t table[]); // expected-error {{cannot use WebAssembly table as a function parameter}}
void illegal_nn_argument_2(__non_null_externref_t table[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
void illegal_nn_argument_3(__non_null_externref_t *table); // expected-error {{pointer to WebAssembly reference type is not allowed}}
void illegal_nn_argument_4(__non_null_externref_t ***table); // expected-error {{pointer to WebAssembly reference type is not allowed}}
void illegal_nn_argument_5(__non_null_externref_t (*table)[0]); // expected-error {{cannot form a pointer to a WebAssembly table}}
void illegal_nn_argument_6(__non_null_externref_t table[0]); // expected-error {{cannot use WebAssembly table as a function parameter}}

__externref_t *illegal_return_1(); // expected-error {{pointer to WebAssembly reference type is not allowed}}
__externref_t ***illegal_return_2(); // expected-error {{pointer to WebAssembly reference type is not allowed}}
__externref_t (*illegal_return_3())[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}

__non_null_externref_t *illegal_nn_return_1(); // expected-error {{pointer to WebAssembly reference type is not allowed}}
__non_null_externref_t ***illegal_nn_return_2(); // expected-error {{pointer to WebAssembly reference type is not allowed}}
__non_null_externref_t (*illegal_nn_return_3())[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}

void varargs(int, ...);
typedef void (*__funcref funcref_t)();
Expand Down Expand Up @@ -133,3 +184,15 @@ void foo() {
void *ret_void_ptr() {
return table; // expected-error {{cannot return a WebAssembly table}}
}

// checks all related assignment from extern ref to non null extern ref and vice versa
void externref_assignment(__externref_t er, __non_null_externref_t nn_er) {
__externref_t asg_1 = er;
__externref_t asg_2 = nn_er;

__non_null_externref_t nn_asg_1 = er; // conly-error {{initializing '__non_null_externref_t' with an expression of incompatible type '__externref_t'}} \
// cpp-error {{cannot initialize a variable of type '__non_null_externref_t' with an lvalue of type '__externref_t'}}
__non_null_externref_t nn_asg_2 = nn_er;


}
1 change: 1 addition & 0 deletions llvm/include/llvm/CodeGen/ValueTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ def untyped : ValueType<8, 231> { // Produces an untyped value
}
def funcref : ValueType<0, 232>; // WebAssembly's funcref type
def externref : ValueType<0, 233>; // WebAssembly's externref type
def externref_nn : ValueType<0, 242>; // WebAssembly's nonnull externref type
def exnref : ValueType<0, 234>; // WebAssembly's exnref type
def x86amx : ValueType<8192, 235>; // X86 AMX value
def i64x8 : ValueType<512, 236>; // 8 Consecutive GPRs (AArch64)
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ def llvm_v16f64_ty : LLVMType<v16f64>; // 16 x double
def llvm_vararg_ty : LLVMType<isVoid>; // this means vararg here

def llvm_externref_ty : LLVMType<externref>;
def llvm_externref_nn_ty : LLVMType<externref_nn>;
def llvm_funcref_ty : LLVMType<funcref>;
def llvm_exnref_ty : LLVMType<exnref>;

Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/IR/IntrinsicsWebAssembly.td
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def int_wasm_memory_grow :
//===----------------------------------------------------------------------===//
def int_wasm_ref_null_extern :
DefaultAttrsIntrinsic<[llvm_externref_ty], [], [IntrNoMem]>;

// TODO: Is this correct?
def int_wasm_ref_nonnull_extern
: DefaultAttrsIntrinsic<[llvm_externref_nn_ty], [], [IntrNoMem]>;

def int_wasm_ref_null_func :
DefaultAttrsIntrinsic<[llvm_funcref_ty], [], [IntrNoMem]>;
def int_wasm_ref_null_exn:
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ class Type {
// Convenience methods for getting pointer types.
//
LLVM_ABI static Type *getWasm_ExternrefTy(LLVMContext &C);
LLVM_ABI static Type *getWasm_NonNullExternrefTy(LLVMContext &C);
LLVM_ABI static Type *getWasm_FuncrefTy(LLVMContext &C);

/// Return a pointer to the current type. This is equivalent to
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/IR/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@ Type *Type::getWasm_ExternrefTy(LLVMContext &C) {
return Ty;
}

Type *Type::getWasm_NonNullExternrefTy(LLVMContext &C) {
// opaque pointer in addrspace(11)
static PointerType *Ty = PointerType::get(C, 11);
return Ty;
}

Type *Type::getWasm_FuncrefTy(LLVMContext &C) {
// opaque pointer in addrspace(20)
static PointerType *Ty = PointerType::get(C, 20);
Expand Down