diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index 1ecff84dcb..c3f61f18da 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -273,6 +273,9 @@ TargetABI *TargetABI::getTarget() { case llvm::Triple::loongarch64: return getLoongArch64TargetABI(); #endif // LDC_LLVM_VER >= 1600 + case llvm::Triple::wasm32: + case llvm::Triple::wasm64: + return getWasmTargetABI(); default: Logger::cout() << "WARNING: Unknown ABI, guessing...\n"; return new UnknownTargetABI; diff --git a/gen/abi/targets.h b/gen/abi/targets.h index e913b657ca..49098fe257 100644 --- a/gen/abi/targets.h +++ b/gen/abi/targets.h @@ -38,3 +38,5 @@ TargetABI *getX86_64TargetABI(); TargetABI *getX86TargetABI(); TargetABI *getLoongArch64TargetABI(); + +TargetABI *getWasmTargetABI(); diff --git a/gen/abi/wasm.cpp b/gen/abi/wasm.cpp new file mode 100644 index 0000000000..52669a538d --- /dev/null +++ b/gen/abi/wasm.cpp @@ -0,0 +1,77 @@ +//===-- wasm.cpp ----------------------------------------------------------===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// see https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md +// +//===----------------------------------------------------------------------===// + +#include "gen/abi/generic.h" + +using namespace dmd; + +namespace { +Type *getSingleWrappedScalarType(Type *t) { + t = t->toBasetype(); + + if (auto ts = t->isTypeStruct()) { + if (ts->sym->fields.length != 1) + return nullptr; + return getSingleWrappedScalarType(ts->sym->fields[0]->type); + } + + if (auto tsa = t->isTypeSArray()) { + if (tsa->dim->toInteger() != 1) + return nullptr; + return getSingleWrappedScalarType(tsa->nextOf()); + } + + return t->isscalar() + // some more pointers: + || t->ty == TY::Tclass || t->ty == TY::Taarray + ? t + : nullptr; +} +} + +struct WasmTargetABI : TargetABI { + static bool isDirectlyPassedAggregate(Type *t) { + // Structs and static arrays are generally passed byval, except for POD + // aggregates wrapping a single scalar type. + + if (!DtoIsInMemoryOnly(t)) // not a struct or static array + return false; + + // max scalar type size is 16 (`real`); return early if larger + if (size(t) > 16 || !isPOD(t)) + return false; + + Type *singleWrappedScalarType = getSingleWrappedScalarType(t); + return singleWrappedScalarType && + // not passed directly if over-aligned + DtoAlignment(t) <= DtoAlignment(singleWrappedScalarType); + } + + bool returnInArg(TypeFunction *tf, bool) override { + if (tf->isref()) { + return false; + } + + Type *rt = tf->next->toBasetype(); + return passByVal(tf, rt); + } + + bool passByVal(TypeFunction *, Type *t) override { + return DtoIsInMemoryOnly(t) && !isDirectlyPassedAggregate(t); + } + + void rewriteFunctionType(IrFuncTy &) override {} +}; + +// The public getter for abi.cpp. +TargetABI *getWasmTargetABI() { return new WasmTargetABI; } diff --git a/gen/target.cpp b/gen/target.cpp index 3b141fd1e9..30f9e7c972 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -60,6 +60,10 @@ llvm::Type *getRealType(const llvm::Triple &triple) { #endif // LDC_LLVM_VER >= 1600 return LLType::getFP128Ty(ctx); + case Triple::wasm32: + case Triple::wasm64: + return LLType::getFP128Ty(ctx); + default: // 64-bit double precision for all other targets // FIXME: PowerPC, SystemZ, ... diff --git a/tests/codegen/emscripten.d b/tests/codegen/emscripten.d index c2e02754df..74505912e5 100644 --- a/tests/codegen/emscripten.d +++ b/tests/codegen/emscripten.d @@ -1,6 +1,7 @@ // REQUIRES: target_WebAssembly -// RUN: %ldc -mtriple=wasm32-unknown-emscripten -c %s +// RUN: %ldc -mtriple=wasm32-unknown-emscripten -output-s -of=%t.s %s +// RUN: FileCheck %s < %t.s // test predefined versions: @@ -19,3 +20,13 @@ import core.stdc.stdio; extern(C) void _start() { puts("Hello world"); } + + +// ABI smoke test for directly passed aggregates: + +struct S { + double[1] x; +} + +// CHECK: .functype _D10emscripten3fooFSQs1SZQg (f64) -> (f64) +S foo(S s) { return s; }