Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -1970,6 +1970,7 @@ /// Check if this is a WebAssembly Reference Type. bool isWebAssemblyReferenceType() const; + bool isWebAssemblyExternrefType() 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 Index: clang/include/clang/Basic/BuiltinsWebAssembly.def =================================================================== --- clang/include/clang/Basic/BuiltinsWebAssembly.def +++ clang/include/clang/Basic/BuiltinsWebAssembly.def @@ -195,5 +195,11 @@ TARGET_BUILTIN(__builtin_wasm_ref_null_extern, "e", "nc", "reference-types") TARGET_BUILTIN(__builtin_wasm_ref_null_func, "k", "nc", "reference-types") +// Table builtins +TARGET_BUILTIN(__builtin_wasm_table_size, "ii", "nt", "reference-types") +TARGET_BUILTIN(__builtin_wasm_table_grow, "iiii", "nt", "reference-types") +TARGET_BUILTIN(__builtin_wasm_table_fill, "viiii", "t", "reference-types") +TARGET_BUILTIN(__builtin_wasm_table_copy, "viiiii", "t", "reference-types") + #undef BUILTIN #undef TARGET_BUILTIN Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11720,4 +11720,10 @@ "WebAssembly table must be static">; def err_wasm_multidimensional_ref_array : Error< "multi-dimensional arrays of WebAssembly references are illegal">; +def err_wasm_builtin_arg_must_be_table_type : Error < + "%ordinal0 argument must be a WebAssembly table">; +def err_wasm_builtin_arg_must_match_table_element_type : Error < + "%ordinal0 argument must match the element type of the WebAssembly table in the %ordinal1 argument">; +def err_wasm_builtin_arg_must_be_integer_type : Error < + "%ordinal0 argument must be an integer">; } // end of sema component. Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -13100,6 +13100,9 @@ bool CheckRISCVLMUL(CallExpr *TheCall, unsigned ArgNum); bool CheckRISCVBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall); + bool CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, + unsigned BuiltinID, + CallExpr *TheCall); bool SemaBuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall); bool SemaBuiltinVAStartARMMicrosoft(CallExpr *Call); @@ -13163,6 +13166,11 @@ ExprResult SemaBuiltinMatrixColumnMajorStore(CallExpr *TheCall, ExprResult CallResult); + bool SemaBuiltinWasmTableSize(CallExpr *TheCall); + bool SemaBuiltinWasmTableGrow(CallExpr *TheCall); + bool SemaBuiltinWasmTableFill(CallExpr *TheCall); + bool SemaBuiltinWasmTableCopy(CallExpr *TheCall); + public: enum FormatStringType { FST_Scanf, Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -2325,6 +2325,10 @@ bool Type::isSizelessType() const { return isSizelessBuiltinType(); } bool Type::isWebAssemblyReferenceType() const { + return isWebAssemblyExternrefType(); +} + +bool Type::isWebAssemblyExternrefType() const { const BuiltinType *BT = getAs(); if (BT && BT->getKind() == BuiltinType::WasmExternRef) return true; Index: clang/lib/CodeGen/CGBuiltin.cpp =================================================================== --- clang/lib/CodeGen/CGBuiltin.cpp +++ clang/lib/CodeGen/CGBuiltin.cpp @@ -18837,6 +18837,60 @@ CGM.getIntrinsic(Intrinsic::wasm_dot_i8x16_i7x16_add_signed); return Builder.CreateCall(Callee, {LHS, RHS, Acc}); } + case WebAssembly::BI__builtin_wasm_table_size: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *Value = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_table_size); + return Builder.CreateCall(Callee, Value); + } + case WebAssembly::BI__builtin_wasm_table_grow: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *Table = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Value *Val = EmitScalarExpr(E->getArg(1)); + Value *NElems = EmitScalarExpr(E->getArg(2)); + + Function *Callee; + if (E->getArg(1)->getType()->isWebAssemblyExternrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_grow_externref); + else + llvm_unreachable( + "Unexpected reference type for __builtin_wasm_table_grow"); + + return Builder.CreateCall(Callee, {Table, Val, NElems}); + } + case WebAssembly::BI__builtin_wasm_table_fill: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *Table = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Value *Index = EmitScalarExpr(E->getArg(1)); + Value *Val = EmitScalarExpr(E->getArg(2)); + Value *NElems = EmitScalarExpr(E->getArg(3)); + + Function *Callee; + if (E->getArg(2)->getType()->isWebAssemblyExternrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_fill_externref); + else + llvm_unreachable( + "Unexpected reference type for __builtin_wasm_table_fill"); + + return Builder.CreateCall(Callee, {Table, Index, Val, NElems}); + } + case WebAssembly::BI__builtin_wasm_table_copy: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *TableX = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Value *TableY = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(1)).getPointer()); + Value *DstIdx = EmitScalarExpr(E->getArg(2)); + Value *SrcIdx = EmitScalarExpr(E->getArg(3)); + Value *NElems = EmitScalarExpr(E->getArg(4)); + + Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_table_copy); + + return Builder.CreateCall(Callee, {TableX, TableY, SrcIdx, DstIdx, NElems}); + } default: return nullptr; } Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -1981,6 +1981,9 @@ case llvm::Triple::riscv32: case llvm::Triple::riscv64: return CheckRISCVBuiltinFunctionCall(TI, BuiltinID, TheCall); + case llvm::Triple::wasm32: + case llvm::Triple::wasm64: + return CheckWebAssemblyBuiltinFunctionCall(TI, BuiltinID, TheCall); } } @@ -4514,6 +4517,23 @@ return SemaBuiltinConstantArgRange(TheCall, i, l, u); } +bool Sema::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, + unsigned BuiltinID, + CallExpr *TheCall) { + switch (BuiltinID) { + case WebAssembly::BI__builtin_wasm_table_size: + return SemaBuiltinWasmTableSize(TheCall); + case WebAssembly::BI__builtin_wasm_table_grow: + return SemaBuiltinWasmTableGrow(TheCall); + case WebAssembly::BI__builtin_wasm_table_fill: + return SemaBuiltinWasmTableFill(TheCall); + case WebAssembly::BI__builtin_wasm_table_copy: + return SemaBuiltinWasmTableCopy(TheCall); + } + + return false; +} + /// SemaBuiltinCpuSupports - Handle __builtin_cpu_supports(char *). /// This checks that the target supports __builtin_cpu_supports and /// that the string argument is constant and valid. @@ -17868,6 +17888,125 @@ return CallResult; } +/// Checks the argument at the given index is a WebAssembly table and if it +/// is, sets ElTy to the element type. +static bool CheckWasmBuiltinArgIsTable(Sema &S, CallExpr *E, unsigned ArgIndex, + const Type *&ElTy) { + Expr *ArgExpr = E->getArg(ArgIndex); + ElTy = ArgExpr->getType()->getArrayElementTypeNoTypeQual(); + if (!ElTy || !ElTy->isWebAssemblyReferenceType()) { + return S.Diag(ArgExpr->getBeginLoc(), + diag::err_wasm_builtin_arg_must_be_table_type) + << ArgIndex + 1 << ArgExpr->getSourceRange(); + } + return false; +} + +/// Checks the argument at the given index is an integer. +static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E, + unsigned ArgIndex) { + Expr *ArgExpr = E->getArg(ArgIndex); + if (!ArgExpr->getType()->isIntegerType()) { + return S.Diag(ArgExpr->getBeginLoc(), + diag::err_wasm_builtin_arg_must_be_integer_type) + << ArgIndex + 1 << ArgExpr->getSourceRange(); + } + return false; +} + +/// Check that the argument is a WebAssembly table. +bool Sema::SemaBuiltinWasmTableSize(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 1)) + return true; + + const Type *ElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, ElTy)) + return true; + + return false; +} + +/// Check that the first argument is a WebAssembly table, the second is the +/// value to use for new elements (of a type matching the table type), the +/// third value is an integer. +bool Sema::SemaBuiltinWasmTableGrow(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 3)) + return true; + + const Type *ElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, ElTy)) + return true; + + Expr *NewElemArg = TheCall->getArg(1); + if (!Context.hasSameType(ElTy, NewElemArg->getType().getTypePtr())) { + return Diag(NewElemArg->getBeginLoc(), + diag::err_wasm_builtin_arg_must_match_table_element_type) + << 2 << 1 << NewElemArg->getSourceRange(); + } + + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, 2)) + return true; + + return false; +} + +/// Check that the first argument is a WebAssembly table, the second is an +/// integer, the third is the value to use to fill the table (of a type +/// matching the table type), and the fourth is an integer. +bool Sema::SemaBuiltinWasmTableFill(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 4)) + return true; + + const Type *ElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, ElTy)) + return true; + + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, 1)) + return true; + + Expr *NewElemArg = TheCall->getArg(2); + if (!Context.hasSameType(ElTy, NewElemArg->getType().getTypePtr())) { + return Diag(NewElemArg->getBeginLoc(), + diag::err_wasm_builtin_arg_must_match_table_element_type) + << 3 << 1 << NewElemArg->getSourceRange(); + } + + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, 3)) + return true; + + return false; +} + +/// Check that the first argument is a WebAssembly table, the second is also a +/// WebAssembly table (of the same element type), and the third to fifth +/// arguments are integers. +bool Sema::SemaBuiltinWasmTableCopy(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 5)) + return true; + + const Type *XElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, XElTy)) + return true; + + const Type *YElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 1, YElTy)) + return true; + + Expr *TableYArg = TheCall->getArg(1); + if (!Context.hasSameType(XElTy, YElTy)) { + return Diag(TableYArg->getBeginLoc(), + diag::err_wasm_builtin_arg_must_match_table_element_type) + << 2 << 1 << TableYArg->getSourceRange(); + } + + for (int I = 2; I <= 4; I++) { + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, I)) + return true; + } + + return false; +} + /// \brief Enforce the bounds of a TCB /// CheckTCBEnforcement - Enforces that every function in a named TCB only /// directly calls other functions in the same TCB as marked by the enforce_tcb Index: clang/test/CodeGen/WebAssembly/builtins-table.c =================================================================== --- /dev/null +++ clang/test/CodeGen/WebAssembly/builtins-table.c @@ -0,0 +1,47 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature +// RUN: %clang_cc1 -triple wasm32 -target-feature +reference-types -disable-O0-optnone -emit-llvm %s -o - | opt -S -mem2reg | FileCheck %s +// REQUIRES: webassembly-registered-target + +static __externref_t table[0]; + +// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_size +// CHECK-SAME: () #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.wasm.table.size(ptr addrspace(1) @table) +// CHECK-NEXT: ret i32 [[TMP0]] +// +int test_builtin_wasm_table_size() { + return __builtin_wasm_table_size(table); +} + +// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow +// CHECK-SAME: (ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @table, ptr addrspace(10) [[REF]], i32 [[NELEM]]) +// CHECK-NEXT: ret i32 [[TMP0]] +// +int test_builtin_wasm_table_grow(__externref_t ref, int nelem) { + return __builtin_wasm_table_grow(table, ref, nelem); +} + +// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill +// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.wasm.table.fill.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]], i32 [[NELEM]]) +// CHECK-NEXT: ret void +// +void test_builtin_wasm_table_fill(int index, __externref_t ref, int nelem) { + __builtin_wasm_table_fill(table, index, ref, nelem); +} + +static __externref_t other_table[0]; + +// CHECK-LABEL: define {{[^@]+}}@test_table_copy +// CHECK-SAME: (i32 noundef [[DST_IDX:%.*]], i32 noundef [[SRC_IDX:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.wasm.table.copy(ptr addrspace(1) @table, ptr addrspace(1) @other_table, i32 [[SRC_IDX]], i32 [[DST_IDX]], i32 [[NELEM]]) +// CHECK-NEXT: ret void +// +void test_table_copy(int dst_idx, int src_idx, int nelem) { + __builtin_wasm_table_copy(table, other_table, dst_idx, src_idx, nelem); +} Index: clang/test/Sema/builtins-wasm.c =================================================================== --- /dev/null +++ clang/test/Sema/builtins-wasm.c @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -triple wasm32 -target-feature +reference-types %s + +static __externref_t table[0]; + +void test_table_size(__externref_t ref, void *ptr, int arr[]) { + __builtin_wasm_table_size(); // expected-error {{too few arguments to function call, expected 1, have 0}} + __builtin_wasm_table_size(1); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_size(ref); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_size(ptr); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_size(arr); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_size(table, table); // expected-error {{too many arguments to function call, expected 1, have 2}} + void *a = __builtin_wasm_table_size(table); // expected-warning {{incompatible integer to pointer conversion initializing 'void *' with an expression of type 'int'}} + __externref_t b = __builtin_wasm_table_size(table); // expected-error {{initializing '__externref_t' with an expression of incompatible type 'int'}} + int c = __builtin_wasm_table_size(table); +} + +void test_table_grow(__externref_t ref, int size) { + __builtin_wasm_table_grow(); // expected-error {{too few arguments to function call, expected 3, have 0}} + __builtin_wasm_table_grow(table, table, table, table); // expected-error {{too many arguments to function call, expected 3, have 4}} + __builtin_wasm_table_grow(ref, ref, size); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_grow(table, table, size); // expected-error {{2nd argument must match the element type of the WebAssembly table in the 1st argument}} + __builtin_wasm_table_grow(table, ref, table); // expected-error {{3rd argument must be an integer}} + void *a = __builtin_wasm_table_grow(table, ref, size); // expected-warning {{incompatible integer to pointer conversion initializing 'void *' with an expression of type 'int'}} + int b = __builtin_wasm_table_grow(table, ref, size); +} + +void test_table_fill(int index, __externref_t ref, int nelem) { + __builtin_wasm_table_fill(); // expected-error {{too few arguments to function call, expected 4, have 0}} + __builtin_wasm_table_fill(table, table, table, table, table); // expected-error {{too many arguments to function call, expected 4, have 5}} + __builtin_wasm_table_fill(index, index, ref, nelem); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_fill(table, table, ref, nelem); // expected-error {{2nd argument must be an integer}} + __builtin_wasm_table_fill(table, index, index, ref); // expected-error {{3rd argument must match the element type of the WebAssembly table in the 1st argument}} + __builtin_wasm_table_fill(table, index, ref, table); // expected-error {{4th argument must be an integer}} + void *a = __builtin_wasm_table_fill(table, index, ref, nelem); // expected-error {{initializing 'void *' with an expression of incompatible type 'void'}} + __builtin_wasm_table_fill(table, index, ref, nelem); +} + +void test_table_copy(int dst_idx, int src_idx, int nelem) { + __builtin_wasm_table_copy(); // expected-error {{too few arguments to function call, expected 5, have 0}} + __builtin_wasm_table_copy(table, table, table, table, table, table); // expected-error {{too many arguments to function call, expected 5, have 6}} + __builtin_wasm_table_copy(src_idx, table, dst_idx, src_idx, nelem); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_copy(table, src_idx, dst_idx, src_idx, nelem); // expected-error {{2nd argument must be a WebAssembly table}} + __builtin_wasm_table_copy(table, table, table, src_idx, nelem); // expected-error {{3rd argument must be an integer}} + __builtin_wasm_table_copy(table, table, dst_idx, table, nelem); // expected-error {{4th argument must be an integer}} + __builtin_wasm_table_copy(table, table, dst_idx, src_idx, table); // expected-error {{5th argument must be an integer}} + __builtin_wasm_table_copy(table, table, dst_idx, src_idx, nelem); +}