Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Add the asmjs-unknown-emscripten triple #1

Closed
wants to merge 2 commits into from
Closed
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
117 changes: 117 additions & 0 deletions lib/Basic/Targets.cpp
Original file line number Diff line number Diff line change
@@ -239,6 +239,39 @@ class DragonFlyBSDTargetInfo : public OSTargetInfo<Target> {
}
};

// Emscripten target
template<typename Target>
class EmscriptenTargetInfo : public OSTargetInfo<Target> {
protected:
virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
MacroBuilder &Builder) const {
// A macro for the platform.
Builder.defineMacro("__EMSCRIPTEN__");
// Earlier versions of Emscripten defined this, so we continue to define it
// for compatibility, for now. Users should ideally prefer __EMSCRIPTEN__.
Builder.defineMacro("EMSCRIPTEN");
// A common platform macro.
if (Opts.POSIXThreads)
Builder.defineMacro("_REENTRANT");
// Follow g++ convention and predefine _GNU_SOURCE for C++.
if (Opts.CPlusPlus)
Builder.defineMacro("_GNU_SOURCE");

// Emscripten's software environment and the asm.js runtime aren't really
// Unix per se, but they're perhaps more Unix-like than what software
// expects when "unix" is *not* defined.
DefineStd(Builder, "unix", Opts);
}
public:
explicit EmscriptenTargetInfo(const std::string& triple)
: OSTargetInfo<Target>(triple) {
// Emcripten currently does prepend a prefix to user labels, but this is
// handled outside of clang. TODO: Handling this within clang may be
// beneficial.
this->UserLabelPrefix = "";
}
};

// FreeBSD Target
template<typename Target>
class FreeBSDTargetInfo : public OSTargetInfo<Target> {
@@ -5041,6 +5074,82 @@ class Mips64ELTargetInfo : public Mips64TargetInfoBase {
};
} // end anonymous namespace.

namespace {
class AsmJSTargetInfo : public TargetInfo {
public:
explicit AsmJSTargetInfo(const std::string& triple) : TargetInfo(triple) {
BigEndian = false;
this->LongAlign = 32;
this->LongWidth = 32;
this->PointerAlign = 32;
this->PointerWidth = 32;
this->IntMaxType = TargetInfo::SignedLongLong;
this->UIntMaxType = TargetInfo::UnsignedLongLong;
this->Int64Type = TargetInfo::SignedLongLong;
this->DoubleAlign = 64;
this->LongDoubleWidth = 64;
this->LongDoubleAlign = 64;
this->SizeType = TargetInfo::UnsignedInt;
this->PtrDiffType = TargetInfo::SignedInt;
this->IntPtrType = TargetInfo::SignedInt;
this->RegParmMax = 0; // Disallow regparm

// Set the native integer widths set to just i32, since that's currently
// the only integer type we can do arithmetic on without masking or
// splitting.
//
// Set the required alignment for 128-bit vectors to just 4 bytes, based on
// the direction suggested here:
// https://bugzilla.mozilla.org/show_bug.cgi?id=904913#c21
// We can still set the preferred alignment to 16 bytes though.
DescriptionString = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"
"f32:32:32-f64:64:64-p:32:32:32-v128:32:128-n32";
}

void getDefaultFeatures(llvm::StringMap<bool> &Features) const {
}
virtual void getArchDefines(const LangOptions &Opts,
MacroBuilder &Builder) const {
Builder.defineMacro("__asmjs__");
}
virtual void getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const {
Builder.defineMacro("__LITTLE_ENDIAN__");
getArchDefines(Opts, Builder);
}
virtual void getTargetBuiltins(const Builtin::Info *&Records,
unsigned &NumRecords) const {
}
virtual BuiltinVaListKind getBuiltinVaListKind() const {
// Reuse PNaCl's va_list lowering.
return TargetInfo::PNaClABIBuiltinVaList;
}
virtual void getGCCRegNames(const char * const *&Names,
unsigned &NumNames) const {
Names = NULL;
NumNames = 0;
}
virtual void getGCCRegAliases(const GCCRegAlias *&Aliases,
unsigned &NumAliases) const {
Aliases = NULL;
NumAliases = 0;
}
virtual bool validateAsmConstraint(const char *&Name,
TargetInfo::ConstraintInfo &Info) const {
return false;
}
virtual const char *getClobbers() const {
return "";
}
virtual bool isCLZForZeroUndef() const {
// Today we do clz in software, so we just do the right thing. With ES6,
// we'll get Math.clz32, which is to be defined to do the right thing:
// http://esdiscuss.org/topic/rename-number-prototype-clz-to-math-clz#content-36
return false;
}
};
} // end anonymous namespace.

namespace {
class PNaClTargetInfo : public TargetInfo {
public:
@@ -5315,6 +5424,14 @@ static TargetInfo *AllocateTarget(const std::string &T) {
return new Mips64ELTargetInfo(T);
}

case llvm::Triple::asmjs:
switch (os) {
case llvm::Triple::Emscripten:
return new EmscriptenTargetInfo<AsmJSTargetInfo>(T);
default:
return NULL;
}

case llvm::Triple::le32:
switch (os) {
case llvm::Triple::NaCl:
10 changes: 6 additions & 4 deletions lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
@@ -232,15 +232,17 @@ CodeGen::CGCXXABI *CodeGen::CreateItaniumCXXABI(CodeGenModule &CGM) {
/* UseARMGuardVarABI = */ true);

case TargetCXXABI::GenericItanium:
if (CGM.getContext().getTargetInfo().getTriple().getArch()
== llvm::Triple::le32) {
// For PNaCl, use ARM-style method pointers so that PNaCl code
switch (CGM.getContext().getTargetInfo().getTriple().getArch()) {
case llvm::Triple::le32:
case llvm::Triple::asmjs:
// Use ARM-style method pointers so that generated code
// does not assume anything about the alignment of function
// pointers.
return new ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ true,
/* UseARMGuardVarABI = */ false);
default:
return new ItaniumCXXABI(CGM);
}
return new ItaniumCXXABI(CGM);

case TargetCXXABI::Microsoft:
llvm_unreachable("Microsoft ABI is not Itanium-based");
59 changes: 59 additions & 0 deletions lib/CodeGen/TargetInfo.cpp
Original file line number Diff line number Diff line change
@@ -410,6 +410,62 @@ ABIArgInfo DefaultABIInfo::classifyReturnType(QualType RetTy) const {
ABIArgInfo::getExtend() : ABIArgInfo::getDirect());
}

//===----------------------------------------------------------------------===//
// Emscripten ABI Implementation
//
// This is a very simple ABI that relies a lot on DefaultABIInfo.
//===----------------------------------------------------------------------===//

class EmscriptenABIInfo : public DefaultABIInfo {
public:
explicit EmscriptenABIInfo(CodeGen::CodeGenTypes &CGT) : DefaultABIInfo(CGT) {}

ABIArgInfo classifyReturnType(QualType RetTy) const;
ABIArgInfo classifyArgumentType(QualType RetTy) const;
};

class EmscriptenTargetCodeGenInfo : public TargetCodeGenInfo {
public:
explicit EmscriptenTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT)
: TargetCodeGenInfo(new EmscriptenABIInfo(CGT)) {}

// TODO: Re-evaluate whether these hacks, borrowed from PNaCl, are necessary.
bool addAsmMemoryAroundSyncSynchronize() const { return true; }
bool asmMemoryIsFence() const { return true; }
};

/// \brief Classify argument of given type \p Ty.
ABIArgInfo EmscriptenABIInfo::classifyArgumentType(QualType Ty) const {
if (isAggregateTypeForABI(Ty)) {
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT))
return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory);
return ABIArgInfo::getIndirect(0);
}

// We can handle floating-point values directly.
if (Ty->isFloatingType())
return ABIArgInfo::getDirect();

// Otherwise just do the default thing.
return DefaultABIInfo::classifyArgumentType(Ty);
}

ABIArgInfo EmscriptenABIInfo::classifyReturnType(QualType RetTy) const {
if (isAggregateTypeForABI(RetTy)) {
// As an optimization, lower single-element structs to just return a
// regular value.
if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
}

// We can handle floating-point values directly.
if (RetTy->isFloatingType())
return ABIArgInfo::getDirect();

// Otherwise just do the default thing.
return DefaultABIInfo::classifyReturnType(RetTy);
}

//===----------------------------------------------------------------------===//
// le32/PNaCl bitcode ABI Implementation
//
@@ -5101,6 +5157,9 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
default:
return *(TheTargetCodeGenInfo = new DefaultTargetCodeGenInfo(Types));

case llvm::Triple::asmjs:
return *(TheTargetCodeGenInfo = new EmscriptenTargetCodeGenInfo(Types));

case llvm::Triple::le32:
return *(TheTargetCodeGenInfo = new PNaClTargetCodeGenInfo(Types));
case llvm::Triple::mips:
3 changes: 3 additions & 0 deletions lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
@@ -1759,6 +1759,9 @@ const ToolChain &Driver::getToolChain(const ArgList &Args,
case llvm::Triple::Win32:
TC = new toolchains::Windows(*this, Target, Args);
break;
case llvm::Triple::Emscripten:
TC = new toolchains::EmscriptenToolChain(*this, Target, Args);
break;
case llvm::Triple::MinGW32:
// FIXME: We need a MinGW toolchain. Fallthrough for now.
default:
30 changes: 30 additions & 0 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
@@ -1738,6 +1738,36 @@ bool TCEToolChain::isPICDefaultForced() const {
return false;
}

/// EmscriptenToolChain - A tool chain for the Emscripten C/C++ to JS compiler.

EmscriptenToolChain::EmscriptenToolChain(const Driver &D, const llvm::Triple& Triple,
const ArgList &Args)
: ToolChain(D, Triple, Args) {
}

EmscriptenToolChain::~EmscriptenToolChain() {
}

bool EmscriptenToolChain::IsMathErrnoDefault() const {
return false;
}

bool EmscriptenToolChain::IsObjCNonFragileABIDefault() const {
return true;
}

bool EmscriptenToolChain::isPICDefault() const {
return false;
}

bool EmscriptenToolChain::isPIEDefault() const {
return false;
}

bool EmscriptenToolChain::isPICDefaultForced() const {
return false;
}

/// OpenBSD - OpenBSD tool chain which can call as(1) and ld(1) directly.

OpenBSD::OpenBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args)
14 changes: 14 additions & 0 deletions lib/Driver/ToolChains.h
Original file line number Diff line number Diff line change
@@ -577,6 +577,20 @@ class LLVM_LIBRARY_VISIBILITY TCEToolChain : public ToolChain {
bool isPICDefaultForced() const;
};

/// EmscriptenToolChain - A tool chain for the Emscripten C/C++ to JS compiler.
class LLVM_LIBRARY_VISIBILITY EmscriptenToolChain : public ToolChain {
public:
EmscriptenToolChain(const Driver &D, const llvm::Triple& Triple,
const ArgList &Args);
~EmscriptenToolChain();

bool IsMathErrnoDefault() const;
bool IsObjCNonFragileABIDefault() const;
bool isPICDefault() const;
bool isPIEDefault() const;
bool isPICDefaultForced() const;
};

class LLVM_LIBRARY_VISIBILITY Windows : public ToolChain {
public:
Windows(const Driver &D, const llvm::Triple& Triple, const ArgList &Args);
61 changes: 61 additions & 0 deletions test/CodeGen/emscripten-arguments.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// RUN: %clang_cc1 -triple asmjs-unknown-emscripten %s -emit-llvm -o - | FileCheck %s

// Basic argument/attribute tests for asmjs/Emscripten

// CHECK: define void @f0(i32 %i, i32 %j, double %k)
void f0(int i, long j, double k) {}

typedef struct {
int aa;
int bb;
} s1;
// Structs should be passed byval and not split up
// CHECK: define void @f1(%struct.s1* byval %i)
void f1(s1 i) {}

typedef struct {
int cc;
} s2;
// Structs should be returned sret and not simplified by the frontend
// CHECK: define void @f2(%struct.s2* noalias sret %agg.result)
s2 f2() {
s2 foo;
return foo;
}

// CHECK: define void @f3(i64 %i)
void f3(long long i) {}

// i8/i16 should be signext, i32 and higher should not
// CHECK: define void @f4(i8 signext %a, i16 signext %b)
void f4(char a, short b) {}

// CHECK: define void @f5(i8 zeroext %a, i16 zeroext %b)
void f5(unsigned char a, unsigned short b) {}


enum my_enum {
ENUM1,
ENUM2,
ENUM3,
};
// Enums should be treated as the underlying i32
// CHECK: define void @f6(i32 %a)
void f6(enum my_enum a) {}

union simple_union {
int a;
char b;
};
// Unions should be passed as byval structs
// CHECK: define void @f7(%union.simple_union* byval %s)
void f7(union simple_union s) {}

typedef struct {
int b4 : 4;
int b3 : 3;
int b8 : 8;
} bitfield1;
// Bitfields should be passed as byval structs
// CHECK: define void @f8(%struct.bitfield1* byval %bf1)
void f8(bitfield1 bf1) {}
4 changes: 4 additions & 0 deletions test/CodeGen/emscripten-regparm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// RUN: %clang_cc1 -triple asmjs-unknown-emscripten %s -fsyntax-only -verify

void __attribute__((regparm(2))) fc_f1(int i, int j, int k) {} // expected-error{{'regparm' is not valid on this platform}}

2 changes: 2 additions & 0 deletions test/CodeGenCXX/member-function-pointers.cpp
Original file line number Diff line number Diff line change
@@ -6,6 +6,8 @@

// PNaCl uses the same representation of method pointers as ARM.
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=le32-unknown-nacl | FileCheck -check-prefix GLOBAL-ARM %s
// Emscripten uses the same representation of method pointers as ARM.
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=asmjs-unknown-emscripten | FileCheck -check-prefix GLOBAL-ARM %s

struct A { int a; void f(); virtual void vf1(); virtual void vf2(); };
struct B { int b; virtual void g(); };
14 changes: 14 additions & 0 deletions test/CodeGenCXX/static-init-emscripten.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %clang_cc1 -emit-llvm -triple=asmjs-unknown-emscripten -o - %s | FileCheck %s

int f();

// Test that Emscripten uses the Itanium/x86 ABI in which the static
// variable's guard variable is tested via "load i8 and compare with
// zero" rather than the ARM ABI which uses "load i32 and test the
// bottom bit".
void g() {
static int a = f();
}
// CHECK: load atomic i8* bitcast (i64* @_ZGVZ1gvE1a to i8*) acquire
// CHECK-NEXT: %guard.uninitialized = icmp eq i8 %0, 0
// CHECK-NEXT: br i1 %guard.uninitialized, label %init.check, label %init.end
146 changes: 146 additions & 0 deletions test/Driver/asmjs-unknown-emscripten.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// RUN: %clang -target asmjs-unknown-emscripten -ccc-echo %s -emit-llvm-only -c 2>&1 | FileCheck %s -check-prefix=ECHO
// RUN: %clang -target asmjs-unknown-emscripten %s -emit-llvm -S -c -o - | FileCheck %s
// RUN: %clang -target asmjs-unknown-emscripten %s -emit-llvm -S -c -pthread -o - | FileCheck %s -check-prefix=THREADS

// ECHO: {{.*}} -cc1 {{.*}}asmjs-unknown-emscripten.c

// Check platform defines
#include <stdarg.h>
#include <stddef.h>

extern "C" {

// CHECK: @align_c = global i32 1
int align_c = __alignof(char);

// CHECK: @align_s = global i32 2
int align_s = __alignof(short);

// CHECK: @align_i = global i32 4
int align_i = __alignof(int);

// CHECK: @align_l = global i32 4
int align_l = __alignof(long);

// CHECK: @align_ll = global i32 8
int align_ll = __alignof(long long);

// CHECK: @align_p = global i32 4
int align_p = __alignof(void*);

// CHECK: @align_f = global i32 4
int align_f = __alignof(float);

// CHECK: @align_d = global i32 8
int align_d = __alignof(double);

// CHECK: @align_ld = global i32 8
int align_ld = __alignof(long double);

// CHECK: @align_vl = global i32 4
int align_vl = __alignof(va_list);

// CHECK: __LITTLE_ENDIAN__defined
#ifdef __LITTLE_ENDIAN__
void __LITTLE_ENDIAN__defined() {}
#endif

// CHECK: __native_client__defined
#ifdef __native_client__
void __native_client__defined() {}
#endif

// CHECK: __le32__defined
#ifdef __le32__
void __le32__defined() {}
#endif

// CHECK: __pnacl__defined
#ifdef __pnacl__
void __pnacl__defined() {}
#endif

// CHECK: unixdefined
#ifdef unix
void unixdefined() {}
#endif

// CHECK: __ELF__defined
#ifdef __ELF__
void __ELF__defined() {}
#endif

// CHECK: _GNU_SOURCEdefined
#ifdef _GNU_SOURCE
void _GNU_SOURCEdefined() {}
#endif

// THREADS: _REENTRANTdefined
// CHECK: _REENTRANTundefined
#ifdef _REENTRANT
void _REENTRANTdefined() {}
#else
void _REENTRANTundefined() {}
#endif

// Check types

// CHECK: signext i8 @check_char()
char check_char() { return 0; }

// CHECK: signext i16 @check_short()
short check_short() { return 0; }

// CHECK: i32 @check_int()
int check_int() { return 0; }

// CHECK: i32 @check_long()
long check_long() { return 0; }

// CHECK: i64 @check_longlong()
long long check_longlong() { return 0; }

// CHECK: zeroext i8 @check_uchar()
unsigned char check_uchar() { return 0; }

// CHECK: zeroext i16 @check_ushort()
unsigned short check_ushort() { return 0; }

// CHECK: i32 @check_uint()
unsigned int check_uint() { return 0; }

// CHECK: i32 @check_ulong()
unsigned long check_ulong() { return 0; }

// CHECK: i64 @check_ulonglong()
unsigned long long check_ulonglong() { return 0; }

// CHECK: i32 @check_size_t()
size_t check_size_t() { return 0; }

// CHECK: float @check_float()
float check_float() { return 0; }

// CHECK: double @check_double()
double check_double() { return 0; }

// CHECK: double @check_longdouble()
long double check_longdouble() { return 0; }

}

template<int> void Switch();
template<> void Switch<4>();
template<> void Switch<8>();
template<> void Switch<16>();

void check_pointer_size() {
// CHECK: SwitchILi4
Switch<sizeof(void*)>();

// CHECK: SwitchILi8
Switch<sizeof(long long)>();

// CHECK: SwitchILi16
Switch<sizeof(va_list)>();
}