aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2014-02-25 12:25:39 -0800
committerAlon Zakai <alonzakai@gmail.com>2014-02-25 12:25:39 -0800
commit2a5c6e40d0a9d279750f84a3678c3e4d23061301 (patch)
tree7714a0b54ab9b3fb492b0c79505671b1310ff8d8
parentec1178a11d6dbeb371f45dd94a1d1c0849bbf1cb (diff)
parent632c823b8bfd3fd5a4b77628eddd6161c5d8367e (diff)
Merge branch 'master' of github.com:sunfishcode/emscripten-fastcomp-clang into incoming
-rw-r--r--lib/Basic/Targets.cpp117
-rw-r--r--lib/CodeGen/ItaniumCXXABI.cpp10
-rw-r--r--lib/CodeGen/TargetInfo.cpp59
-rw-r--r--lib/Driver/Driver.cpp3
-rw-r--r--lib/Driver/ToolChains.cpp30
-rw-r--r--lib/Driver/ToolChains.h14
-rw-r--r--test/CodeGen/emscripten-arguments.c61
-rw-r--r--test/CodeGen/emscripten-regparm.c4
-rw-r--r--test/CodeGenCXX/member-function-pointers.cpp2
-rw-r--r--test/CodeGenCXX/static-init-emscripten.cpp14
-rw-r--r--test/Driver/asmjs-unknown-emscripten.cpp146
11 files changed, 456 insertions, 4 deletions
diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp
index a622a11aa5..43a21f2734 100644
--- a/lib/Basic/Targets.cpp
+++ b/lib/Basic/Targets.cpp
@@ -239,6 +239,39 @@ public:
}
};
+// 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> {
@@ -5042,6 +5075,82 @@ public:
} // 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:
PNaClTargetInfo(const std::string& triple) : TargetInfo(triple) {
@@ -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:
diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp
index 8a7f551e68..3767e4f278 100644
--- a/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/lib/CodeGen/ItaniumCXXABI.cpp
@@ -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");
diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp
index 4fb888a767..5aa982e255 100644
--- a/lib/CodeGen/TargetInfo.cpp
+++ b/lib/CodeGen/TargetInfo.cpp
@@ -411,6 +411,62 @@ ABIArgInfo DefaultABIInfo::classifyReturnType(QualType RetTy) const {
}
//===----------------------------------------------------------------------===//
+// 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
//
// This is a simplified version of the x86_32 ABI. Arguments and return values
@@ -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:
diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp
index 1dbbc9a342..6d0ac5733a 100644
--- a/lib/Driver/Driver.cpp
+++ b/lib/Driver/Driver.cpp
@@ -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:
diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
index fffba0e4e5..2f3cf15490 100644
--- a/lib/Driver/ToolChains.cpp
+++ b/lib/Driver/ToolChains.cpp
@@ -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)
diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h
index 3afd8dd228..50973073da 100644
--- a/lib/Driver/ToolChains.h
+++ b/lib/Driver/ToolChains.h
@@ -577,6 +577,20 @@ public:
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);
diff --git a/test/CodeGen/emscripten-arguments.c b/test/CodeGen/emscripten-arguments.c
new file mode 100644
index 0000000000..afa0d9c57b
--- /dev/null
+++ b/test/CodeGen/emscripten-arguments.c
@@ -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) {}
diff --git a/test/CodeGen/emscripten-regparm.c b/test/CodeGen/emscripten-regparm.c
new file mode 100644
index 0000000000..20ffc9dec0
--- /dev/null
+++ b/test/CodeGen/emscripten-regparm.c
@@ -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}}
+
diff --git a/test/CodeGenCXX/member-function-pointers.cpp b/test/CodeGenCXX/member-function-pointers.cpp
index 23a7b0009f..c29f0e75e5 100644
--- a/test/CodeGenCXX/member-function-pointers.cpp
+++ b/test/CodeGenCXX/member-function-pointers.cpp
@@ -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(); };
diff --git a/test/CodeGenCXX/static-init-emscripten.cpp b/test/CodeGenCXX/static-init-emscripten.cpp
new file mode 100644
index 0000000000..b9d0271ae1
--- /dev/null
+++ b/test/CodeGenCXX/static-init-emscripten.cpp
@@ -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
diff --git a/test/Driver/asmjs-unknown-emscripten.cpp b/test/Driver/asmjs-unknown-emscripten.cpp
new file mode 100644
index 0000000000..fb94f0ecf9
--- /dev/null
+++ b/test/Driver/asmjs-unknown-emscripten.cpp
@@ -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)>();
+}