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
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
117 changes: 117 additions & 0 deletions lib/Basic/Targets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
10 changes: 6 additions & 4 deletions lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
59 changes: 59 additions & 0 deletions lib/CodeGen/TargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
30 changes: 30 additions & 0 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions lib/Driver/ToolChains.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
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
Expand Up @@ -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(); };
Expand Down
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
Loading