diff --git a/dmd2/globals.h b/dmd2/globals.h index 2ff4b19812e..eeaa22648f2 100644 --- a/dmd2/globals.h +++ b/dmd2/globals.h @@ -128,6 +128,7 @@ struct Param bool addMain; // LDC_FIXME: Implement. bool allInst; // LDC_FIXME: Implement. unsigned nestedTmpl; // maximum nested template instantiations + bool betterC; // be a "better C" compiler; no dependency on D runtime #else bool pic; // generate position-independent-code for shared libs bool color; // use ANSI colors in console output @@ -136,7 +137,6 @@ struct Param bool nofloat; // code should not pull in floating point support bool ignoreUnsupportedPragmas; // rather than error on them bool enforcePropertySyntax; - bool betterC; // be a "better C" compiler; no dependency on D runtime bool addMain; // add a default main() function bool allInst; // generate code for all template instantiations #endif diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 173c3a09dac..ec47e4a26ad 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -408,6 +408,11 @@ cl::opt cl::desc("implement http://wiki.dlang.org/DIP25 (experimental)"), cl::location(global.params.useDIP25)); +cl::opt betterC( + "betterC", + cl::desc("omit generating some runtime information and helper functions"), + cl::location(global.params.betterC)); + cl::opt coverageAnalysis( "cov", cl::desc("Compile-in code coverage analysis\n(use -cov=n for n% " "minimum required coverage)"), diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp index 2988a3ed517..5ccaa765d4d 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -159,6 +159,7 @@ Usage:\n\ files.d D source files\n\ @cmdfile read arguments from cmdfile\n\ -allinst generate code for all template instantiations\n\ + -betterC omit generating some runtime information and helper functions\n\ -c do not link\n\ -color[=on|off] force colored console output on or off\n\ -conf=path use config file at path\n\ diff --git a/driver/main.cpp b/driver/main.cpp index 6b27493ca48..a6ad0474d76 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -459,12 +459,10 @@ static void parseCommandLine(int argc, char **argv, Strings &sourceFiles, } if (noDefaultLib) { - deprecation( - Loc(), - "-nodefaultlib is deprecated, as " - "-defaultlib/-debuglib now override the existing list instead of " - "appending to it. Please use the latter instead."); - } else { + deprecation(Loc(), "-nodefaultlib is deprecated, as -defaultlib/-debuglib " + "now override the existing list instead of appending to " + "it. Please use the latter instead."); + } else if (!global.params.betterC) { // Parse comma-separated default library list. std::stringstream libNames(linkDebugLib ? debugLib : defaultLib); while (libNames.good()) { @@ -860,6 +858,10 @@ static void registerPredefinedVersions() { VersionCondition::addPredefinedGlobalIdent("D_NoBoundsChecks"); } + if (global.params.betterC) { + VersionCondition::addPredefinedGlobalIdent("D_BetterC"); + } + registerPredefinedTargetVersions(); // Pass sanitizer arguments to linker. Requires clang. diff --git a/gen/aa.cpp b/gen/aa.cpp index 587cde57dc3..76c497dfbcc 100644 --- a/gen/aa.cpp +++ b/gen/aa.cpp @@ -12,6 +12,7 @@ #include "declaration.h" #include "module.h" #include "mtype.h" +#include "gen/arrays.h" #include "gen/dvalue.h" #include "gen/irstate.h" #include "gen/llvm.h" @@ -91,14 +92,7 @@ DValue *DtoAAIndex(Loc &loc, Type *type, DValue *aa, DValue *key, bool lvalue) { gIR->scope() = IRScope(failbb); - llvm::Function *errorfn = - getRuntimeFunction(loc, gIR->module, "_d_arraybounds"); - gIR->CreateCallOrInvoke( - errorfn, DtoModuleFileName(gIR->func()->decl->getModule(), loc), - DtoConstUint(loc.linnum)); - - // the function does not return - gIR->ir->CreateUnreachable(); + DtoBoundsCheckFailCall(gIR, loc); // if ok, proceed in okbb gIR->scope() = IRScope(okbb); diff --git a/gen/arrays.cpp b/gen/arrays.cpp index e96d0e34a78..463bd54528f 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -1216,12 +1216,17 @@ void DtoIndexBoundsCheck(Loc &loc, DValue *arr, DValue *index) { } void DtoBoundsCheckFailCall(IRState *irs, Loc &loc) { - llvm::Function *errorfn = - getRuntimeFunction(loc, irs->module, "_d_arraybounds"); - irs->CreateCallOrInvoke( - errorfn, DtoModuleFileName(irs->func()->decl->getModule(), loc), - DtoConstUint(loc.linnum)); - - // the function does not return - irs->ir->CreateUnreachable(); + Module *const module = irs->func()->decl->getModule(); + + if (global.params.betterC) { + DtoCAssert(module, loc, DtoConstCString("array overflow")); + } else { + llvm::Function *errorfn = + getRuntimeFunction(loc, irs->module, "_d_arraybounds"); + irs->CreateCallOrInvoke(errorfn, DtoModuleFileName(module, loc), + DtoConstUint(loc.linnum)); + + // the function does not return + irs->ir->CreateUnreachable(); + } } diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 19417a40d3c..e9d42927e71 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -265,11 +265,50 @@ void DtoAssert(Module *M, Loc &loc, DValue *msg) { gIR->ir->CreateUnreachable(); } +void DtoCAssert(Module *M, Loc &loc, LLValue *msg) { + const auto file = DtoConstCString(loc.filename ? loc.filename + : M->srcfile->name->toChars()); + const auto line = DtoConstUint(loc.linnum); + const auto fn = getCAssertFunction(loc, gIR->module); + + llvm::SmallVector args; + if (global.params.targetTriple.isOSDarwin()) { + const auto irFunc = gIR->func(); + const auto funcName = + irFunc && irFunc->decl ? irFunc->decl->toPrettyChars() : ""; + args.push_back(DtoConstCString(funcName)); + args.push_back(file); + args.push_back(line); + args.push_back(msg); + } else if (global.params.targetTriple.isOSSolaris()) { + const auto irFunc = gIR->func(); + const auto funcName = + (irFunc && irFunc->decl) ? irFunc->decl->toPrettyChars() : ""; + args.push_back(msg); + args.push_back(file); + args.push_back(line); + args.push_back(DtoConstCString(funcName)); + } else if (global.params.targetTriple.getEnvironment() == + llvm::Triple::Android) { + args.push_back(file); + args.push_back(line); + args.push_back(msg); + } else { + args.push_back(msg); + args.push_back(file); + args.push_back(line); + } + + gIR->func()->scopes->callOrInvoke(fn, args); + + gIR->ir->CreateUnreachable(); +} + /****************************************************************************** * MODULE FILE NAME ******************************************************************************/ -LLValue *DtoModuleFileName(Module *M, const Loc &loc) { +LLConstant *DtoModuleFileName(Module *M, const Loc &loc) { return DtoConstString(loc.filename ? loc.filename : M->srcfile->name->toChars()); } diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index b9934966a9c..37667d62380 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -56,9 +56,10 @@ LLValue *DtoAllocaDump(LLValue *val, LLType *asType, int alignment = 0, // assertion generator void DtoAssert(Module *M, Loc &loc, DValue *msg); +void DtoCAssert(Module *M, Loc &loc, LLValue *msg); // returns module file name -LLValue *DtoModuleFileName(Module *M, const Loc &loc); +LLConstant *DtoModuleFileName(Module *M, const Loc &loc); /// emits goto to LabelStatement with the target identifier void DtoGoto(Loc &loc, LabelDsymbol *target); diff --git a/gen/module.cpp b/gen/module.cpp index cc04cf807c7..57b02cbdab1 100644 --- a/gen/module.cpp +++ b/gen/module.cpp @@ -743,8 +743,8 @@ void codegenModule(IRState *irs, Module *m, bool emitFullModuleInfo) { } // Skip emission of all the additional module metadata if requested by the - // user. - if (!m->noModuleInfo) { + // user or the betterC switch is on. + if (!global.params.betterC && !m->noModuleInfo) { // generate ModuleInfo genModuleInfo(m, emitFullModuleInfo); diff --git a/gen/runtime.cpp b/gen/runtime.cpp index e112a2f1846..5d9b1385788 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -178,6 +178,43 @@ llvm::GlobalVariable *getRuntimeGlobal(Loc &loc, llvm::Module &target, g->getLinkage(), nullptr, g->getName()); } +// C assert function: +// OSX: void __assert_rtn(const char *func, const char *file, unsigned line, +// const char *msg) +// Android: void __assert(const char *file, int line, const char *msg) +// MSVC: void _assert(const char *msg, const char *file, unsigned line) +// Solaris: void __assert_c99(const char *assertion, const char *filename, int line_num, +// const char *funcname); +// else: void __assert(const char *msg, const char *file, unsigned line) + +static const char *getCAssertFunctionName() { + if (global.params.targetTriple.isOSDarwin()) { + return "__assert_rtn"; + } else if (global.params.targetTriple.isWindowsMSVCEnvironment()) { + return "_assert"; + } else if (global.params.targetTriple.isOSSolaris()) { + return "__assert_c99"; + } + return "__assert"; +} + +static std::vector getCAssertFunctionParamTypes() { + const auto voidPtr = Type::tvoidptr; + const auto uint = Type::tuns32; + + if (global.params.targetTriple.isOSDarwin() || global.params.targetTriple.isOSSolaris()) { + return {voidPtr, voidPtr, uint, voidPtr}; + } + if (global.params.targetTriple.getEnvironment() == llvm::Triple::Android) { + return {voidPtr, uint, voidPtr}; + } + return {voidPtr, voidPtr, uint}; +} + +llvm::Function *getCAssertFunction(const Loc &loc, llvm::Module &target) { + return getRuntimeFunction(loc, target, getCAssertFunctionName()); +} + //////////////////////////////////////////////////////////////////////////////// // extern (D) alias dg_t = int delegate(void*); @@ -347,6 +384,11 @@ static void buildRuntimeModule() { ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + // C assert function + createFwdDecl(LINKc, Type::tvoid, {getCAssertFunctionName()}, + getCAssertFunctionParamTypes(), {}, + Attr_Cold_NoReturn); + // void _d_assert(string file, uint line) // void _d_arraybounds(string file, uint line) createFwdDecl(LINKc, Type::tvoid, {"_d_assert", "_d_arraybounds"}, diff --git a/gen/runtime.h b/gen/runtime.h index 3c6118391a5..6e5cab2261f 100644 --- a/gen/runtime.h +++ b/gen/runtime.h @@ -31,5 +31,6 @@ llvm::Function *getRuntimeFunction(const Loc &loc, llvm::Module &target, llvm::GlobalVariable * getRuntimeGlobal(const Loc &loc, llvm::Module &target, const char *name); +llvm::Function *getCAssertFunction(const Loc &loc, llvm::Module &target); #endif // LDC_GEN_RUNTIME_H diff --git a/gen/statements.cpp b/gen/statements.cpp index df075a24d08..ff31daa9789 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -1506,11 +1506,17 @@ class ToIRVisitor : public Visitor { stmt->loc.toChars()); LOG_SCOPE; + Module *const module = irs->func()->decl->getModule(); + + if (global.params.betterC) { + DtoCAssert(module, stmt->loc, DtoConstCString("no switch default")); + return; + } + llvm::Function *fn = getRuntimeFunction(stmt->loc, irs->module, "_d_switch_error"); - LLValue *moduleInfoSymbol = - getIrModule(irs->func()->decl->getModule())->moduleInfoSymbol(); + LLValue *moduleInfoSymbol = getIrModule(module)->moduleInfoSymbol(); LLType *moduleInfoType = DtoType(Module::moduleinfo->type); LLCallSite call = irs->CreateCallOrInvoke( diff --git a/gen/toir.cpp b/gen/toir.cpp index 9093040bb94..92a48314007 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -1889,8 +1889,16 @@ class ToElemVisitor : public Visitor { * msg is not evaluated at all. So should use toElemDtor() * instead of toElem(). */ - DtoAssert(p->func()->decl->getModule(), e->loc, - e->msg ? toElemDtor(e->msg) : nullptr); + DValue *const msg = e->msg ? toElemDtor(e->msg) : nullptr; + Module *const module = p->func()->decl->getModule(); + if (global.params.betterC) { + const auto cMsg = + msg ? DtoArrayPtr(msg) // assuming `msg` is null-terminated, like DMD + : DtoConstCString(e->e1->toChars()); + DtoCAssert(module, e->e1->loc, cMsg); + } else { + DtoAssert(module, e->loc, msg); + } // passed: p->scope() = IRScope(passedbb); @@ -2339,6 +2347,17 @@ class ToElemVisitor : public Visitor { e->type->toChars()); LOG_SCOPE; + if (global.params.betterC) { + error( + e->loc, + "array concatenation of expression `%s` requires the GC which is not " + "available with -betterC", + e->toChars()); + result = + new DSliceValue(e->type, llvm::UndefValue::get(DtoType(e->type)), llvm::UndefValue::get(DtoType(e->type))); + return; + } + result = DtoCatArrays(e->loc, e->type, e->e1, e->e2); } diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 7794a124bd9..74a3d6f7652 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -463,12 +463,13 @@ LLConstant *DtoConstFP(Type *t, longdouble value) { //////////////////////////////////////////////////////////////////////////////// -LLConstant *DtoConstString(const char *str) { +LLConstant *DtoConstCString(const char *str) { llvm::StringRef s(str ? str : ""); - llvm::GlobalVariable *gvar = (gIR->stringLiteral1ByteCache.find(s) == - gIR->stringLiteral1ByteCache.end()) - ? nullptr - : gIR->stringLiteral1ByteCache[s]; + + const auto it = gIR->stringLiteral1ByteCache.find(s); + llvm::GlobalVariable *gvar = + it == gIR->stringLiteral1ByteCache.end() ? nullptr : it->getValue(); + if (gvar == nullptr) { llvm::Constant *init = llvm::ConstantDataArray::getString(gIR->context(), s, true); @@ -482,14 +483,19 @@ LLConstant *DtoConstString(const char *str) { #endif gIR->stringLiteral1ByteCache[s] = gvar; } + LLConstant *idxs[] = {DtoConstUint(0), DtoConstUint(0)}; - return DtoConstSlice(DtoConstSize_t(s.size()), - llvm::ConstantExpr::getGetElementPtr( + return llvm::ConstantExpr::getGetElementPtr( #if LDC_LLVM_VER >= 307 gvar->getInitializer()->getType(), #endif - gvar, idxs, true), - Type::tchar->arrayOf()); + gvar, idxs, true); +} + +LLConstant *DtoConstString(const char *str) { + LLConstant *cString = DtoConstCString(str); + LLConstant *length = DtoConstSize_t(str ? strlen(str) : 0); + return DtoConstSlice(length, cString, Type::tchar->arrayOf()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/gen/tollvm.h b/gen/tollvm.h index 169b227131e..27edf66827d 100644 --- a/gen/tollvm.h +++ b/gen/tollvm.h @@ -93,6 +93,7 @@ LLConstantInt *DtoConstInt(int i); LLConstantInt *DtoConstUbyte(unsigned char i); LLConstant *DtoConstFP(Type *t, longdouble value); +LLConstant *DtoConstCString(const char *); LLConstant *DtoConstString(const char *); LLConstant *DtoConstBool(bool); diff --git a/gen/typinf.cpp b/gen/typinf.cpp index b8bd5b4f789..b03d1624003 100644 --- a/gen/typinf.cpp +++ b/gen/typinf.cpp @@ -737,8 +737,8 @@ void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p) { emitTypeMetadata(decl); - // this is a declaration of a builtin __initZ var - if (builtinTypeInfo(decl->tinfo)) { + // check if the definition can be elided + if (global.params.betterC || builtinTypeInfo(decl->tinfo)) { return; } diff --git a/tests/codegen/betterC_typeinfo.d b/tests/codegen/betterC_typeinfo.d new file mode 100644 index 00000000000..12f2814fc5d --- /dev/null +++ b/tests/codegen/betterC_typeinfo.d @@ -0,0 +1,13 @@ +// Make sure the file can be compiled and linked successfully with -betterC. +// Also test that druntime and Phobos aren't in the linker command line. +// RUN: %ldc -betterC %s -v > %t.log +// RUN: FileCheck %s < %t.log +// CHECK-NOT: druntime-ldc +// CHECK-NOT: phobos2-ldc + +struct MyStruct { int a; } + +extern (C) void main() +{ + auto s = MyStruct(); +} diff --git a/tests/linking/betterc.d b/tests/linking/betterc.d new file mode 100644 index 00000000000..a7047188777 --- /dev/null +++ b/tests/linking/betterc.d @@ -0,0 +1,15 @@ +// RUN: %ldc -betterC -c -output-ll -of=%t.ll %s && FileCheck %s < %t.ll +// RUN: %ldc -betterC -defaultlib= -run %s + +// CHECK-NOT: ModuleInfoZ +// CHECK-NOT: ModuleRefZ +// CHECK-NOT: call void @ldc.register_dso +version (CRuntime_Microsoft) { + extern(C) int mainCRTStartup() { + return 0; + } +} + +extern (C) int main() { + return 0; +}