diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..9b3aa8b721 --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index f35e3d6a4a..5c61bcf9cc 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -24,218 +24,177 @@ namespace opts { */ struct CoverageParser : public cl::parser { #if LDC_LLVM_VER >= 307 - CoverageParser(cl::Option &O) : cl::parser(O) {} + CoverageParser(cl::Option &O) : cl::parser(O) {} #endif - bool parse(cl::Option &O, llvm::StringRef ArgName, llvm::StringRef Arg, unsigned char &Val) - { - if (Arg == "") { - Val = 0; - return false; - } - - if (Arg.getAsInteger(0, Val)) - return O.error("'" + Arg + "' value invalid for required coverage percentage"); - - if (Val > 100) { - return O.error("Required coverage percentage must be <= 100"); - } - return false; + bool parse(cl::Option &O, llvm::StringRef ArgName, llvm::StringRef Arg, + unsigned char &Val) { + if (Arg == "") { + Val = 0; + return false; } + + if (Arg.getAsInteger(0, Val)) + return O.error("'" + Arg + + "' value invalid for required coverage percentage"); + + if (Val > 100) { + return O.error("Required coverage percentage must be <= 100"); + } + return false; + } }; - // Positional options first, in order: -cl::list fileList( - cl::Positional, cl::desc("files")); +cl::list fileList(cl::Positional, cl::desc("files")); -cl::list runargs("run", - cl::desc("Runs the resulting program, passing the remaining arguments to it"), - cl::Positional, - cl::PositionalEatsArgs); +cl::list runargs( + "run", + cl::desc( + "Runs the resulting program, passing the remaining arguments to it"), + cl::Positional, cl::PositionalEatsArgs); static cl::opt useDeprecated( - cl::desc("Allow deprecated code/language features:"), - cl::ZeroOrMore, - cl::values( - clEnumValN(0, "de", "Do not allow deprecated features"), - clEnumValN(1, "d", "Silently allow deprecated features"), - clEnumValN(2, "dw", "Warn about the use of deprecated features"), - clEnumValEnd), - cl::location(global.params.useDeprecated), - cl::init(2)); + cl::desc("Allow deprecated code/language features:"), cl::ZeroOrMore, + cl::values(clEnumValN(0, "de", "Do not allow deprecated features"), + clEnumValN(1, "d", "Silently allow deprecated features"), + clEnumValN(2, "dw", "Warn about the use of deprecated features"), + clEnumValEnd), + cl::location(global.params.useDeprecated), cl::init(2)); -cl::opt enforcePropertySyntax("property", - cl::desc("Enforce property syntax"), - cl::ZeroOrMore, - cl::location(global.params.enforcePropertySyntax)); +cl::opt + enforcePropertySyntax("property", cl::desc("Enforce property syntax"), + cl::ZeroOrMore, + cl::location(global.params.enforcePropertySyntax)); -cl::opt compileOnly("c", - cl::desc("Do not link"), - cl::ZeroOrMore); +cl::opt compileOnly("c", cl::desc("Do not link"), cl::ZeroOrMore); -cl::opt createStaticLib("lib", - cl::desc("Create static library"), - cl::ZeroOrMore); +cl::opt createStaticLib("lib", cl::desc("Create static library"), + cl::ZeroOrMore); -cl::opt createSharedLib("shared", - cl::desc("Create shared library"), - cl::ZeroOrMore); +cl::opt createSharedLib("shared", cl::desc("Create shared library"), + cl::ZeroOrMore); -static cl::opt verbose("v", - cl::desc("Verbose"), - cl::ZeroOrMore, - cl::location(global.params.verbose)); +static cl::opt verbose("v", cl::desc("Verbose"), cl::ZeroOrMore, + cl::location(global.params.verbose)); -static cl::opt vcolumns("vcolumns", - cl::desc("print character (column) numbers in diagnostics"), - cl::ZeroOrMore, - cl::location(global.params.showColumns)); +static cl::opt + vcolumns("vcolumns", + cl::desc("print character (column) numbers in diagnostics"), + cl::ZeroOrMore, cl::location(global.params.showColumns)); -static cl::opt vgc("vgc", - cl::desc("list all gc allocations including hidden ones"), - cl::ZeroOrMore, - cl::location(global.params.vgc)); +static cl::opt + vgc("vgc", cl::desc("list all gc allocations including hidden ones"), + cl::ZeroOrMore, cl::location(global.params.vgc)); -static cl::opt verbose_cg("v-cg", - cl::desc("Verbose codegen"), - cl::ZeroOrMore, - cl::location(global.params.verbose_cg)); +static cl::opt verbose_cg("v-cg", cl::desc("Verbose codegen"), + cl::ZeroOrMore, + cl::location(global.params.verbose_cg)); -static cl::opt errorLimit("verrors", +static cl::opt errorLimit( + "verrors", cl::desc("limit the number of error messages (0 means unlimited)"), cl::location(global.errorLimit)); -static cl::opt warnings( - cl::desc("Warnings:"), - cl::ZeroOrMore, - cl::values( - clEnumValN(1, "w", "Enable warnings"), - clEnumValN(2, "wi", "Enable informational warnings"), - clEnumValEnd), - cl::location(global.params.warnings), - cl::init(0)); +static cl::opt + warnings(cl::desc("Warnings:"), cl::ZeroOrMore, + cl::values(clEnumValN(1, "w", "Enable warnings"), + clEnumValN(2, "wi", "Enable informational warnings"), + clEnumValEnd), + cl::location(global.params.warnings), cl::init(0)); -static cl::opt ignoreUnsupportedPragmas("ignore", - cl::desc("Ignore unsupported pragmas"), - cl::ZeroOrMore, +static cl::opt ignoreUnsupportedPragmas( + "ignore", cl::desc("Ignore unsupported pragmas"), cl::ZeroOrMore, cl::location(global.params.ignoreUnsupportedPragmas)); -static cl::opt debugInfo( - cl::desc("Generating debug information:"), - cl::ZeroOrMore, - cl::values( - clEnumValN(1, "g", "Generate debug information"), - clEnumValN(2, "gc", "Same as -g, but pretend to be C"), - clEnumValEnd), - cl::location(global.params.symdebug), - cl::init(0)); +static cl::opt + debugInfo(cl::desc("Generating debug information:"), cl::ZeroOrMore, + cl::values(clEnumValN(1, "g", "Generate debug information"), + clEnumValN(2, "gc", "Same as -g, but pretend to be C"), + clEnumValEnd), + cl::location(global.params.symdebug), cl::init(0)); -cl::opt noAsm("noasm", - cl::desc("Disallow use of inline assembler")); +cl::opt noAsm("noasm", cl::desc("Disallow use of inline assembler")); // Output file options -cl::opt dontWriteObj("o-", - cl::desc("Do not write object file")); +cl::opt dontWriteObj("o-", cl::desc("Do not write object file")); -cl::opt objectFile("of", - cl::value_desc("filename"), - cl::Prefix, - cl::desc("Use as output file name")); +cl::opt objectFile("of", cl::value_desc("filename"), cl::Prefix, + cl::desc("Use as output file name")); -cl::opt objectDir("od", - cl::value_desc("objdir"), - cl::Prefix, - cl::desc("Write object files to directory ")); - -cl::opt soname("soname", - cl::value_desc("soname"), - cl::Hidden, - cl::Prefix, - cl::desc("Use as output shared library soname")); +cl::opt + objectDir("od", cl::value_desc("objdir"), cl::Prefix, + cl::desc("Write object files to directory ")); +cl::opt + soname("soname", cl::value_desc("soname"), cl::Hidden, cl::Prefix, + cl::desc("Use as output shared library soname")); // Output format options -cl::opt output_bc("output-bc", - cl::desc("Write LLVM bitcode")); +cl::opt output_bc("output-bc", cl::desc("Write LLVM bitcode")); -cl::opt output_ll("output-ll", - cl::desc("Write LLVM IR")); +cl::opt output_ll("output-ll", cl::desc("Write LLVM IR")); -cl::opt output_s("output-s", - cl::desc("Write native assembly")); +cl::opt output_s("output-s", cl::desc("Write native assembly")); cl::opt output_o("output-o", - cl::desc("Write native object")); + cl::desc("Write native object")); // Disabling Red Zone -cl::opt disableRedZone("disable-red-zone", - cl::desc("Do not emit code that uses the red zone."), - cl::location(global.params.disableRedZone), - cl::init(false)); +cl::opt + disableRedZone("disable-red-zone", + cl::desc("Do not emit code that uses the red zone."), + cl::location(global.params.disableRedZone), cl::init(false)); // DDoc options -static cl::opt doDdoc("D", - cl::desc("Generate documentation"), - cl::location(global.params.doDocComments)); +static cl::opt doDdoc("D", cl::desc("Generate documentation"), + cl::location(global.params.doDocComments)); -cl::opt ddocDir("Dd", - cl::desc("Write documentation file to directory"), - cl::value_desc("docdir"), - cl::Prefix); +cl::opt + ddocDir("Dd", cl::desc("Write documentation file to directory"), + cl::value_desc("docdir"), cl::Prefix); -cl::opt ddocFile("Df", - cl::desc("Write documentation file to "), - cl::value_desc("filename"), - cl::Prefix); +cl::opt + ddocFile("Df", cl::desc("Write documentation file to "), + cl::value_desc("filename"), cl::Prefix); // Json options -static cl::opt doJson("X", - cl::desc("Generate JSON file"), - cl::location(global.params.doJsonGeneration)); +static cl::opt doJson("X", cl::desc("Generate JSON file"), + cl::location(global.params.doJsonGeneration)); -cl::opt jsonFile("Xf", - cl::desc("Write JSON file to "), - cl::value_desc("filename"), - cl::Prefix); +cl::opt jsonFile("Xf", cl::desc("Write JSON file to "), + cl::value_desc("filename"), cl::Prefix); // Header generation options -static cl::opt doHdrGen("H", - cl::desc("Generate 'header' file"), - cl::location(global.params.doHdrGeneration)); +static cl::opt + doHdrGen("H", cl::desc("Generate 'header' file"), + cl::location(global.params.doHdrGeneration)); -cl::opt hdrDir("Hd", - cl::desc("Write 'header' file to directory"), - cl::value_desc("hdrdir"), - cl::Prefix); +cl::opt + hdrDir("Hd", cl::desc("Write 'header' file to directory"), + cl::value_desc("hdrdir"), cl::Prefix); cl::opt hdrFile("Hf", - cl::desc("Write 'header' file to "), - cl::value_desc("filename"), - cl::Prefix); + cl::desc("Write 'header' file to "), + cl::value_desc("filename"), cl::Prefix); -static cl::opt hdrKeepAllBodies("Hkeep-all-bodies", - cl::desc("Keep all function bodies in .di files"), - cl::ZeroOrMore, - cl::location(global.params.hdrKeepAllBodies)); +static cl::opt hdrKeepAllBodies( + "Hkeep-all-bodies", cl::desc("Keep all function bodies in .di files"), + cl::ZeroOrMore, cl::location(global.params.hdrKeepAllBodies)); static cl::opt unittest("unittest", - cl::desc("Compile in unit tests"), - cl::location(global.params.useUnitTests)); - + cl::desc("Compile in unit tests"), + cl::location(global.params.useUnitTests)); static StringsAdapter strImpPathStore("J", global.params.fileImppath); -static cl::list stringImportPaths("J", - cl::desc("Where to look for string imports"), - cl::value_desc("path"), - cl::location(strImpPathStore), - cl::Prefix); - -static cl::opt addMain("main", - cl::desc("Add empty main() (e.g. for unittesting)"), - cl::ZeroOrMore, - cl::location(global.params.addMain)); +static cl::list + stringImportPaths("J", cl::desc("Where to look for string imports"), + cl::value_desc("path"), cl::location(strImpPathStore), + cl::Prefix); +static cl::opt + addMain("main", cl::desc("Add empty main() (e.g. for unittesting)"), + cl::ZeroOrMore, cl::location(global.params.addMain)); // -d-debug is a bit messy, it has 3 modes: // -d-debug=ident, -d-debug=level and -d-debug (without argument) @@ -244,219 +203,220 @@ static cl::opt addMain("main", std::vector debugArgs; struct D_DebugStorage { - void push_back(const std::string& str) { - if (str.empty()) { - // Bare "-d-debug" has a special meaning. - global.params.useAssert = true; - global.params.useArrayBounds = BOUNDSCHECKon; - global.params.useInvariants = true; - global.params.useIn = true; - global.params.useOut = true; - debugArgs.push_back("1"); - } else { - debugArgs.push_back(str); - } + void push_back(const std::string &str) { + if (str.empty()) { + // Bare "-d-debug" has a special meaning. + global.params.useAssert = true; + global.params.useArrayBounds = BOUNDSCHECKon; + global.params.useInvariants = true; + global.params.useIn = true; + global.params.useOut = true; + debugArgs.push_back("1"); + } else { + debugArgs.push_back(str); } + } }; static D_DebugStorage dds; // -debug is already declared in LLVM (at least, in debug builds), // so we need to be a bit more verbose. -static cl::list debugVersionsOption("d-debug", +static cl::list debugVersionsOption( + "d-debug", cl::desc("Compile in debug code >= or identified by ."), - cl::value_desc("level/idents"), - cl::location(dds), - cl::CommaSeparated, + cl::value_desc("level/idents"), cl::location(dds), cl::CommaSeparated, cl::ValueOptional); - - // -version is also declared in LLVM, so again we need to be a bit more verbose. -cl::list versions("d-version", +cl::list versions( + "d-version", cl::desc("Compile in version code >= or identified by "), - cl::value_desc("level/idents"), - cl::CommaSeparated); - + cl::value_desc("level/idents"), cl::CommaSeparated); static StringsAdapter linkSwitchStore("L", global.params.linkswitches); -static cl::list linkerSwitches("L", - cl::desc("Pass to the linker"), - cl::value_desc("linkerflag"), - cl::location(linkSwitchStore), - cl::Prefix); - - -cl::opt moduleDepsFile("deps", - cl::desc("Write module dependencies to filename"), - cl::value_desc("filename")); +static cl::list + linkerSwitches("L", cl::desc("Pass to the linker"), + cl::value_desc("linkerflag"), cl::location(linkSwitchStore), + cl::Prefix); +cl::opt + moduleDepsFile("deps", cl::desc("Write module dependencies to filename"), + cl::value_desc("filename")); cl::opt mArch("march", - cl::desc("Architecture to generate code for:")); + cl::desc("Architecture to generate code for:")); -cl::opt m32bits("m32", - cl::desc("32 bit target"), - cl::ZeroOrMore); +cl::opt m32bits("m32", cl::desc("32 bit target"), cl::ZeroOrMore); -cl::opt m64bits("m64", - cl::desc("64 bit target"), - cl::ZeroOrMore); +cl::opt m64bits("m64", cl::desc("64 bit target"), cl::ZeroOrMore); -cl::opt mCPU("mcpu", - cl::desc("Target a specific cpu type (-mcpu=help for details)"), - cl::value_desc("cpu-name"), - cl::init("")); +cl::opt + mCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), cl::init("")); -cl::list mAttrs("mattr", - cl::CommaSeparated, - cl::desc("Target specific attributes (-mattr=help for details)"), - cl::value_desc("a1,+a2,-a3,...")); +cl::list + mAttrs("mattr", cl::CommaSeparated, + cl::desc("Target specific attributes (-mattr=help for details)"), + cl::value_desc("a1,+a2,-a3,...")); cl::opt mTargetTriple("mtriple", - cl::desc("Override target triple")); + cl::desc("Override target triple")); #if LDC_LLVM_VER >= 307 -cl::opt mABI("mabi", - cl::desc("The name of the ABI to be targeted from the backend"), - cl::Hidden, - cl::init("")); +cl::opt + mABI("mabi", + cl::desc("The name of the ABI to be targeted from the backend"), + cl::Hidden, cl::init("")); #endif -cl::opt mRelocModel("relocation-model", - cl::desc("Relocation model"), +cl::opt mRelocModel( + "relocation-model", cl::desc("Relocation model"), cl::init(llvm::Reloc::Default), cl::values( clEnumValN(llvm::Reloc::Default, "default", "Target default relocation model"), - clEnumValN(llvm::Reloc::Static, "static", - "Non-relocatable code"), + clEnumValN(llvm::Reloc::Static, "static", "Non-relocatable code"), clEnumValN(llvm::Reloc::PIC_, "pic", "Fully relocatable, position independent code"), clEnumValN(llvm::Reloc::DynamicNoPIC, "dynamic-no-pic", "Relocatable external references, non-relocatable code"), clEnumValEnd)); -cl::opt mCodeModel("code-model", - cl::desc("Code model"), - cl::init(llvm::CodeModel::Default), +cl::opt mCodeModel( + "code-model", cl::desc("Code model"), cl::init(llvm::CodeModel::Default), cl::values( - clEnumValN(llvm::CodeModel::Default, "default", "Target default code model"), + clEnumValN(llvm::CodeModel::Default, "default", + "Target default code model"), clEnumValN(llvm::CodeModel::Small, "small", "Small code model"), clEnumValN(llvm::CodeModel::Kernel, "kernel", "Kernel code model"), clEnumValN(llvm::CodeModel::Medium, "medium", "Medium code model"), clEnumValN(llvm::CodeModel::Large, "large", "Large code model"), clEnumValEnd)); -cl::opt mFloatABI("float-abi", - cl::desc("ABI/operations to use for floating-point types:"), +cl::opt mFloatABI( + "float-abi", cl::desc("ABI/operations to use for floating-point types:"), cl::init(FloatABI::Default), cl::values( - clEnumValN(FloatABI::Default, "default", "Target default floating-point ABI"), - clEnumValN(FloatABI::Soft, "soft", "Software floating-point ABI and operations"), - clEnumValN(FloatABI::SoftFP, "softfp", "Soft-float ABI, but hardware floating-point instructions"), - clEnumValN(FloatABI::Hard, "hard", "Hardware floating-point ABI and instructions"), + clEnumValN(FloatABI::Default, "default", + "Target default floating-point ABI"), + clEnumValN(FloatABI::Soft, "soft", + "Software floating-point ABI and operations"), + clEnumValN(FloatABI::SoftFP, "softfp", + "Soft-float ABI, but hardware floating-point instructions"), + clEnumValN(FloatABI::Hard, "hard", + "Hardware floating-point ABI and instructions"), clEnumValEnd)); -cl::opt disableFpElim("disable-fp-elim", - cl::desc("Disable frame pointer elimination optimization"), - cl::init(false)); +cl::opt + disableFpElim("disable-fp-elim", + cl::desc("Disable frame pointer elimination optimization"), + cl::init(false)); -static cl::opt > asserts("asserts", - cl::desc("(*) Enable assertions"), - cl::value_desc("bool"), - cl::location(global.params.useAssert), - cl::init(true)); +static cl::opt> + asserts("asserts", cl::desc("(*) Enable assertions"), + cl::value_desc("bool"), cl::location(global.params.useAssert), + cl::init(true)); -cl::opt boundsCheck("boundscheck", - cl::desc("Enable array bounds check"), - cl::values( - clEnumValN(BOUNDSCHECKoff, "off", "no array bounds checks"), - clEnumValN(BOUNDSCHECKsafeonly, "safeonly", "array bounds checks for safe functions only"), - clEnumValN(BOUNDSCHECKon, "on", "array bounds checks for all functions"), - clEnumValEnd), +cl::opt boundsCheck( + "boundscheck", cl::desc("Enable array bounds check"), + cl::values(clEnumValN(BOUNDSCHECKoff, "off", "no array bounds checks"), + clEnumValN(BOUNDSCHECKsafeonly, "safeonly", + "array bounds checks for safe functions only"), + clEnumValN(BOUNDSCHECKon, "on", + "array bounds checks for all functions"), + clEnumValEnd), cl::init(BOUNDSCHECKdefault)); -static cl::opt > invariants("invariants", - cl::desc("(*) Enable invariants"), - cl::location(global.params.useInvariants), - cl::init(true)); +static cl::opt> + invariants("invariants", cl::desc("(*) Enable invariants"), + cl::location(global.params.useInvariants), cl::init(true)); -static cl::opt > preconditions("preconditions", - cl::desc("(*) Enable function preconditions"), - cl::location(global.params.useIn), - cl::init(true)); +static cl::opt> + preconditions("preconditions", + cl::desc("(*) Enable function preconditions"), + cl::location(global.params.useIn), cl::init(true)); -static cl::opt > postconditions("postconditions", - cl::desc("(*) Enable function postconditions"), - cl::location(global.params.useOut), - cl::init(true)); +static cl::opt> + postconditions("postconditions", + cl::desc("(*) Enable function postconditions"), + cl::location(global.params.useOut), cl::init(true)); - -static MultiSetter ContractsSetter(false, - &global.params.useIn, &global.params.useOut, NULL); -static cl::opt > contracts("contracts", - cl::desc("(*) Enable function pre- and post-conditions"), - cl::location(ContractsSetter)); +static MultiSetter ContractsSetter(false, &global.params.useIn, + &global.params.useOut, NULL); +static cl::opt> + contracts("contracts", + cl::desc("(*) Enable function pre- and post-conditions"), + cl::location(ContractsSetter)); bool nonSafeBoundsChecks = true; static MultiSetter ReleaseSetter(true, &global.params.useAssert, - &nonSafeBoundsChecks, &global.params.useInvariants, - &global.params.useOut, &global.params.useIn, NULL); -static cl::opt > release("release", - cl::desc("Disables asserts, invariants, contracts and boundscheck"), - cl::location(ReleaseSetter), - cl::ValueDisallowed); + &nonSafeBoundsChecks, + &global.params.useInvariants, + &global.params.useOut, &global.params.useIn, + NULL); +static cl::opt> + release("release", + cl::desc("Disables asserts, invariants, contracts and boundscheck"), + cl::location(ReleaseSetter), cl::ValueDisallowed); -cl::opt singleObj("singleobj", - cl::desc("Create only a single output object file"), - cl::location(global.params.singleObj)); +cl::opt + singleObj("singleobj", cl::desc("Create only a single output object file"), + cl::location(global.params.singleObj)); -cl::opt linkonceTemplates("linkonce-templates", - cl::desc("Use linkonce_odr linkage for template symbols instead of weak_odr"), +cl::opt linkonceTemplates( + "linkonce-templates", + cl::desc( + "Use linkonce_odr linkage for template symbols instead of weak_odr"), cl::ZeroOrMore); -cl::opt disableLinkerStripDead("disable-linker-strip-dead", +cl::opt disableLinkerStripDead( + "disable-linker-strip-dead", cl::desc("Do not try to remove unused symbols during linking"), cl::init(false)); -cl::opt allinst("allinst", - cl::desc("generate code for all template instantiations"), - cl::location(global.params.allInst)); +cl::opt + allinst("allinst", + cl::desc("generate code for all template instantiations"), + cl::location(global.params.allInst)); -cl::opt nestedTemplateDepth("template-depth", - cl::desc("(experimental) set maximum number of nested template instantiations"), - cl::location(global.params.nestedTmpl), - cl::init(500)); +cl::opt nestedTemplateDepth( + "template-depth", + cl::desc( + "(experimental) set maximum number of nested template instantiations"), + cl::location(global.params.nestedTmpl), cl::init(500)); #if LDC_LLVM_VER < 307 -cl::opt > color("color", - cl::desc("Force colored console output"), - cl::location(global.params.color)); +cl::opt> + color("color", cl::desc("Force colored console output"), + cl::location(global.params.color)); #else -void CreateColorOption() -{ - new cl::opt >("color", - cl::desc("Force colored console output"), - cl::location(global.params.color)); +void CreateColorOption() { + new cl::opt>( + "color", cl::desc("Force colored console output"), + cl::location(global.params.color)); } #endif -cl::opt useDIP25("dip25", - cl::desc("implement http://wiki.dlang.org/DIP25 (experimental)"), - cl::location(global.params.useDIP25)); +cl::opt + useDIP25("dip25", + cl::desc("implement http://wiki.dlang.org/DIP25 (experimental)"), + cl::location(global.params.useDIP25)); -cl::opt coverageAnalysis("cov", - cl::desc("Compile-in code coverage analysis\n(use -cov=n for n% minimum required coverage)"), - cl::location(global.params.covPercent), - cl::ValueOptional, - cl::init(127)); +cl::opt coverageAnalysis( + "cov", cl::desc("Compile-in code coverage analysis\n(use -cov=n for n% " + "minimum required coverage)"), + cl::location(global.params.covPercent), cl::ValueOptional, cl::init(127)); -static cl::extrahelp footer("\n" -"-d-debug can also be specified without options, in which case it enables all\n" -"debug checks (i.e. (asserts, boundchecks, contracts and invariants) as well\n" -"as acting as -d-debug=1\n\n" -"Options marked with (*) also have a -disable-FOO variant with inverted\n" -"meaning.\n"); +static cl::extrahelp footer( + "\n" + "-d-debug can also be specified without options, in which case it enables " + "all\n" + "debug checks (i.e. (asserts, boundchecks, contracts and invariants) as " + "well\n" + "as acting as -d-debug=1\n\n" + "Options marked with (*) also have a -disable-FOO variant with inverted\n" + "meaning.\n"); } // namespace opts diff --git a/driver/cl_options.h b/driver/cl_options.h index 648f7fca15..2fefaf719c 100644 --- a/driver/cl_options.h +++ b/driver/cl_options.h @@ -26,64 +26,64 @@ #include "globals.h" namespace opts { - namespace cl = llvm::cl; +namespace cl = llvm::cl; - /* Mostly generated with the following command: - egrep -e '^(cl::|#if|#e)' gen/cl_options.cpp \ - | sed -re 's/^(cl::.*)\(.*$/ extern \1;/' - */ - extern cl::list fileList; - extern cl::list runargs; - extern cl::opt compileOnly; - extern cl::opt enforcePropertySyntax; - extern cl::opt createStaticLib; - extern cl::opt createSharedLib; - extern cl::opt noAsm; - extern cl::opt dontWriteObj; - extern cl::opt objectFile; - extern cl::opt objectDir; - extern cl::opt soname; - extern cl::opt output_bc; - extern cl::opt output_ll; - extern cl::opt output_s; - extern cl::opt output_o; - extern cl::opt disableRedZone; - extern cl::opt ddocDir; - extern cl::opt ddocFile; - extern cl::opt jsonFile; - extern cl::opt hdrDir; - extern cl::opt hdrFile; - extern cl::list versions; - extern cl::opt moduleDepsFile; +/* Mostly generated with the following command: + egrep -e '^(cl::|#if|#e)' gen/cl_options.cpp \ + | sed -re 's/^(cl::.*)\(.*$/ extern \1;/' + */ +extern cl::list fileList; +extern cl::list runargs; +extern cl::opt compileOnly; +extern cl::opt enforcePropertySyntax; +extern cl::opt createStaticLib; +extern cl::opt createSharedLib; +extern cl::opt noAsm; +extern cl::opt dontWriteObj; +extern cl::opt objectFile; +extern cl::opt objectDir; +extern cl::opt soname; +extern cl::opt output_bc; +extern cl::opt output_ll; +extern cl::opt output_s; +extern cl::opt output_o; +extern cl::opt disableRedZone; +extern cl::opt ddocDir; +extern cl::opt ddocFile; +extern cl::opt jsonFile; +extern cl::opt hdrDir; +extern cl::opt hdrFile; +extern cl::list versions; +extern cl::opt moduleDepsFile; - extern cl::opt mArch; - extern cl::opt m32bits; - extern cl::opt m64bits; - extern cl::opt mCPU; - extern cl::list mAttrs; - extern cl::opt mTargetTriple; +extern cl::opt mArch; +extern cl::opt m32bits; +extern cl::opt m64bits; +extern cl::opt mCPU; +extern cl::list mAttrs; +extern cl::opt mTargetTriple; #if LDC_LLVM_VER >= 307 - extern cl::opt mABI; +extern cl::opt mABI; #endif - extern cl::opt mRelocModel; - extern cl::opt mCodeModel; - extern cl::opt disableFpElim; - extern cl::opt mFloatABI; - extern cl::opt singleObj; - extern cl::opt linkonceTemplates; - extern cl::opt disableLinkerStripDead; +extern cl::opt mRelocModel; +extern cl::opt mCodeModel; +extern cl::opt disableFpElim; +extern cl::opt mFloatABI; +extern cl::opt singleObj; +extern cl::opt linkonceTemplates; +extern cl::opt disableLinkerStripDead; - extern cl::opt boundsCheck; - extern bool nonSafeBoundsChecks; +extern cl::opt boundsCheck; +extern bool nonSafeBoundsChecks; - extern cl::opt nestedTemplateDepth; +extern cl::opt nestedTemplateDepth; - // Arguments to -d-debug - extern std::vector debugArgs; - // Arguments to -run +// Arguments to -d-debug +extern std::vector debugArgs; +// Arguments to -run #if LDC_LLVM_VER >= 307 - void CreateColorOption(); +void CreateColorOption(); #endif } #endif diff --git a/driver/codegenerator.cpp b/driver/codegenerator.cpp index 6ce4b64794..200180bcaa 100644 --- a/driver/codegenerator.cpp +++ b/driver/codegenerator.cpp @@ -27,14 +27,15 @@ Module *g_dMainModule = 0; /// Callback to generate a C main() function, invoked by the frontend. void genCmain(Scope *sc) { - if (g_entrypointModule) return; + if (g_entrypointModule) + return; - /* The D code to be generated is provided as D source code in the form of a - * string. - * Note that Solaris, for unknown reasons, requires both a main() and an - * _main() - */ - static utf8_t code[] = "extern(C) {\n\ + /* The D code to be generated is provided as D source code in the form of a + * string. + * Note that Solaris, for unknown reasons, requires both a main() and an + * _main() + */ + static utf8_t code[] = "extern(C) {\n\ int _d_run_main(int argc, char **argv, void* mainFunc);\n\ int _Dmain(char[][] args);\n\ int main(int argc, char **argv) { return _d_run_main(argc, argv, &_Dmain); }\n\ @@ -43,26 +44,26 @@ void genCmain(Scope *sc) { pragma(LDC_no_moduleinfo);\n\ "; - Identifier *id = Id::entrypoint; - Module *m = new Module("__entrypoint.d", id, 0, 0); + Identifier *id = Id::entrypoint; + Module *m = new Module("__entrypoint.d", id, 0, 0); - Parser p(m, code, sizeof(code) / sizeof(code[0]), 0); - p.scanloc = Loc(); - p.nextToken(); - m->members = p.parseModule(); - assert(p.token.value == TOKeof); + Parser p(m, code, sizeof(code) / sizeof(code[0]), 0); + p.scanloc = Loc(); + p.nextToken(); + m->members = p.parseModule(); + assert(p.token.value == TOKeof); - char v = global.params.verbose; - global.params.verbose = 0; - m->importedFrom = m; - m->importAll(0); - m->semantic(); - m->semantic2(); - m->semantic3(); - global.params.verbose = v; + char v = global.params.verbose; + global.params.verbose = 0; + m->importedFrom = m; + m->importAll(0); + m->semantic(); + m->semantic2(); + m->semantic3(); + global.params.verbose = v; - g_entrypointModule = m; - g_dMainModule = sc->module; + g_entrypointModule = m; + g_dMainModule = sc->module; } namespace { @@ -71,14 +72,14 @@ namespace { /// of the first symbol. void emitSymbolAddrGlobal(llvm::Module &lm, const char *symbolName, const char *addrName) { - llvm::Type *voidPtr = - llvm::PointerType::get(llvm::Type::getInt8Ty(lm.getContext()), 0); - llvm::GlobalVariable *targetSymbol = new llvm::GlobalVariable( - lm, voidPtr, false, llvm::GlobalValue::ExternalWeakLinkage, 0, - symbolName); - new llvm::GlobalVariable( - lm, voidPtr, false, llvm::GlobalValue::ExternalLinkage, - llvm::ConstantExpr::getBitCast(targetSymbol, voidPtr), addrName); + llvm::Type *voidPtr = + llvm::PointerType::get(llvm::Type::getInt8Ty(lm.getContext()), 0); + llvm::GlobalVariable *targetSymbol = new llvm::GlobalVariable( + lm, voidPtr, false, llvm::GlobalValue::ExternalWeakLinkage, 0, + symbolName); + new llvm::GlobalVariable( + lm, voidPtr, false, llvm::GlobalValue::ExternalLinkage, + llvm::ConstantExpr::getBitCast(targetSymbol, voidPtr), addrName); } } @@ -86,136 +87,139 @@ namespace ldc { CodeGenerator::CodeGenerator(llvm::LLVMContext &context, bool singleObj) : context_(context), moduleCount_(0), singleObj_(singleObj), ir_(0), firstModuleObjfileName_(0) { - if (!ClassDeclaration::object) { - error(Loc(), "declaration for class Object not found; druntime not " - "configured properly"); - fatal(); - } + if (!ClassDeclaration::object) { + error(Loc(), "declaration for class Object not found; druntime not " + "configured properly"); + fatal(); + } } CodeGenerator::~CodeGenerator() { - if (singleObj_) { - const char *oname; - const char *filename; - if ((oname = global.params.exefile) || - (oname = global.params.objname)) { - filename = FileName::forceExt(oname, global.params.targetTriple.isOSWindows() ? global.obj_ext_alt : global.obj_ext); - if (global.params.objdir) { - filename = FileName::combine(global.params.objdir, - FileName::name(filename)); - } - } else { - filename = firstModuleObjfileName_; - } - - writeAndFreeLLModule(filename); + if (singleObj_) { + const char *oname; + const char *filename; + if ((oname = global.params.exefile) || (oname = global.params.objname)) { + filename = FileName::forceExt( + oname, global.params.targetTriple.isOSWindows() ? global.obj_ext_alt + : global.obj_ext); + if (global.params.objdir) { + filename = + FileName::combine(global.params.objdir, FileName::name(filename)); + } + } else { + filename = firstModuleObjfileName_; } + + writeAndFreeLLModule(filename); + } } void CodeGenerator::prepareLLModule(Module *m) { - if (!firstModuleObjfileName_) { - firstModuleObjfileName_ = m->objfile->name->str; - } - ++moduleCount_; + if (!firstModuleObjfileName_) { + firstModuleObjfileName_ = m->objfile->name->str; + } + ++moduleCount_; - if (singleObj_ && ir_) return; + if (singleObj_ && ir_) + return; - assert(!ir_); + assert(!ir_); - // See http://llvm.org/bugs/show_bug.cgi?id=11479 – just use the source file - // name, as it should not collide with a symbol name used somewhere in the - // module. - ir_ = new IRState(m->srcfile->toChars(), context_); - ir_->module.setTargetTriple(global.params.targetTriple.str()); + // See http://llvm.org/bugs/show_bug.cgi?id=11479 – just use the source file + // name, as it should not collide with a symbol name used somewhere in the + // module. + ir_ = new IRState(m->srcfile->toChars(), context_); + ir_->module.setTargetTriple(global.params.targetTriple.str()); #if LDC_LLVM_VER >= 308 - ir_->module.setDataLayout(*gDataLayout); + ir_->module.setDataLayout(*gDataLayout); #else - ir_->module.setDataLayout(gDataLayout->getStringRepresentation()); + ir_->module.setDataLayout(gDataLayout->getStringRepresentation()); #endif - // TODO: Make ldc::DIBuilder per-Module to be able to emit several CUs for - // singleObj compilations? - ir_->DBuilder.EmitCompileUnit(m); + // TODO: Make ldc::DIBuilder per-Module to be able to emit several CUs for + // singleObj compilations? + ir_->DBuilder.EmitCompileUnit(m); - IrDsymbol::resetAll(); + IrDsymbol::resetAll(); } void CodeGenerator::finishLLModule(Module *m) { - if (singleObj_) return; + if (singleObj_) + return; - m->deleteObjFile(); - writeAndFreeLLModule(m->objfile->name->str); + m->deleteObjFile(); + writeAndFreeLLModule(m->objfile->name->str); } void CodeGenerator::writeAndFreeLLModule(const char *filename) { - ir_->DBuilder.Finalize(); + ir_->DBuilder.Finalize(); - // Add the linker options metadata flag. - ir_->module.addModuleFlag( - llvm::Module::AppendUnique, "Linker Options", - llvm::MDNode::get(ir_->context(), ir_->LinkerMetadataArgs)); + // Add the linker options metadata flag. + ir_->module.addModuleFlag( + llvm::Module::AppendUnique, "Linker Options", + llvm::MDNode::get(ir_->context(), ir_->LinkerMetadataArgs)); - // Emit ldc version as llvm.ident metadata. - llvm::NamedMDNode *IdentMetadata = - ir_->module.getOrInsertNamedMetadata("llvm.ident"); - std::string Version("ldc version "); - Version.append(global.ldc_version); + // Emit ldc version as llvm.ident metadata. + llvm::NamedMDNode *IdentMetadata = + ir_->module.getOrInsertNamedMetadata("llvm.ident"); + std::string Version("ldc version "); + Version.append(global.ldc_version); #if LDC_LLVM_VER >= 306 - llvm::Metadata *IdentNode[] = + llvm::Metadata *IdentNode[] = #else - llvm::Value *IdentNode[] = + llvm::Value *IdentNode[] = #endif - {llvm::MDString::get(ir_->context(), Version)}; - IdentMetadata->addOperand(llvm::MDNode::get(ir_->context(), IdentNode)); + {llvm::MDString::get(ir_->context(), Version)}; + IdentMetadata->addOperand(llvm::MDNode::get(ir_->context(), IdentNode)); - writeModule(&ir_->module, filename); - global.params.objfiles->push(const_cast(filename)); - delete ir_; - ir_ = 0; + writeModule(&ir_->module, filename); + global.params.objfiles->push(const_cast(filename)); + delete ir_; + ir_ = 0; } void CodeGenerator::emit(Module *m) { - bool const loggerWasEnabled = Logger::enabled(); - if (m->llvmForceLogging && !loggerWasEnabled) { - Logger::enable(); + bool const loggerWasEnabled = Logger::enabled(); + if (m->llvmForceLogging && !loggerWasEnabled) { + Logger::enable(); + } + + IF_LOG Logger::println("CodeGenerator::emit(%s)", m->toPrettyChars()); + LOG_SCOPE; + + if (global.params.verbose_cg) { + printf("codegen: %s (%s)\n", m->toPrettyChars(), m->srcfile->toChars()); + } + + if (global.errors) { + Logger::println("Aborting because of errors"); + fatal(); + } + + prepareLLModule(m); + + // If we are compiling to a single object file then only the first module + // needs to generate a call to _d_dso_registry(). All other modules only add + // a module reference. + // FIXME Find better name. + const bool emitFullModuleInfo = + !singleObj_ || (singleObj_ && moduleCount_ == 1); + codegenModule(ir_, m, emitFullModuleInfo); + if (m == g_dMainModule) { + codegenModule(ir_, g_entrypointModule, emitFullModuleInfo); + + // On Linux, strongly define the excecutabe BSS bracketing symbols in + // the main module for druntime use (see rt.sections_linux). + if (global.params.isLinux) { + emitSymbolAddrGlobal(ir_->module, "__bss_start", "_d_execBssBegAddr"); + emitSymbolAddrGlobal(ir_->module, "_end", "_d_execBssEndAddr"); } + } - IF_LOG Logger::println("CodeGenerator::emit(%s)", m->toPrettyChars()); - LOG_SCOPE; + finishLLModule(m); - if (global.params.verbose_cg) { - printf("codegen: %s (%s)\n", m->toPrettyChars(), m->srcfile->toChars()); - } - - if (global.errors) { - Logger::println("Aborting because of errors"); - fatal(); - } - - prepareLLModule(m); - - // If we are compiling to a single object file then only the first module needs - // to generate a call to _d_dso_registry(). All other modules only add a module - // reference. - // FIXME Find better name. - const bool emitFullModuleInfo = !singleObj_ || (singleObj_ && moduleCount_ == 1); - codegenModule(ir_, m, emitFullModuleInfo); - if (m == g_dMainModule) { - codegenModule(ir_, g_entrypointModule, emitFullModuleInfo); - - // On Linux, strongly define the excecutabe BSS bracketing symbols in - // the main module for druntime use (see rt.sections_linux). - if (global.params.isLinux) { - emitSymbolAddrGlobal(ir_->module, "__bss_start", - "_d_execBssBegAddr"); - emitSymbolAddrGlobal(ir_->module, "_end", "_d_execBssEndAddr"); - } - } - - finishLLModule(m); - - if (m->llvmForceLogging && !loggerWasEnabled) { - Logger::disable(); - } + if (m->llvmForceLogging && !loggerWasEnabled) { + Logger::disable(); + } } } diff --git a/driver/codegenerator.h b/driver/codegenerator.h index 359083e3b1..c24248e048 100644 --- a/driver/codegenerator.h +++ b/driver/codegenerator.h @@ -26,20 +26,20 @@ namespace ldc { class CodeGenerator { public: - CodeGenerator(llvm::LLVMContext &context, bool singleObj); - ~CodeGenerator(); - void emit(Module *m); + CodeGenerator(llvm::LLVMContext &context, bool singleObj); + ~CodeGenerator(); + void emit(Module *m); private: - void prepareLLModule(Module *m); - void finishLLModule(Module *m); - void writeAndFreeLLModule(const char *filename); + void prepareLLModule(Module *m); + void finishLLModule(Module *m); + void writeAndFreeLLModule(const char *filename); - llvm::LLVMContext &context_; - int moduleCount_; - bool const singleObj_; - IRState *ir_; - const char *firstModuleObjfileName_; + llvm::LLVMContext &context_; + int moduleCount_; + bool const singleObj_; + IRState *ir_; + const char *firstModuleObjfileName_; }; } diff --git a/driver/configfile.cpp b/driver/configfile.cpp index c419624b98..fb8b84199f 100644 --- a/driver/configfile.cpp +++ b/driver/configfile.cpp @@ -30,214 +30,196 @@ namespace sys = llvm::sys; // dummy only; needs to be parsed manually earlier as the switches contained in // the config file are injected into the command line options fed to the parser -llvm::cl::opt clConf("conf", - llvm::cl::desc("Use configuration file "), - llvm::cl::value_desc("filename")); +llvm::cl::opt + clConf("conf", llvm::cl::desc("Use configuration file "), + llvm::cl::value_desc("filename")); #if _WIN32 std::string getUserHomeDirectory() { char buff[MAX_PATH]; - HRESULT res = SHGetFolderPathA(NULL, - CSIDL_FLAG_CREATE | CSIDL_APPDATA, - NULL, - SHGFP_TYPE_CURRENT, - buff); + HRESULT res = SHGetFolderPathA(NULL, CSIDL_FLAG_CREATE | CSIDL_APPDATA, NULL, + SHGFP_TYPE_CURRENT, buff); if (res != S_OK) assert(0 && "Failed to get user home directory"); return buff; } #else std::string getUserHomeDirectory() { - const char* home = getenv("HOME"); + const char *home = getenv("HOME"); return home ? home : "/"; } #endif #if _WIN32 -static bool ReadPathFromRegistry(llvm::SmallString<128> &p) -{ - HKEY hkey; - bool res = false; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, - "SOFTWARE\\ldc-developers\\LDC\\0.11.0", //FIXME Version number should be a define - NULL, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) - { - DWORD length; - if (RegGetValue(hkey, NULL, "Path", RRF_RT_REG_SZ, NULL, NULL, &length) == ERROR_SUCCESS) - { - char *data = static_cast(_alloca(length)); - if (RegGetValue(hkey, NULL, "Path", RRF_RT_REG_SZ, NULL, data, &length) == ERROR_SUCCESS) - { - p = std::string(data); - res = true; - } - } - RegCloseKey(hkey); +static bool ReadPathFromRegistry(llvm::SmallString<128> &p) { + HKEY hkey; + bool res = false; + // FIXME: Version number should be a define. + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\ldc-developers\\LDC\\0.11.0", + NULL, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) { + DWORD length; + if (RegGetValue(hkey, NULL, "Path", RRF_RT_REG_SZ, NULL, NULL, &length) == + ERROR_SUCCESS) { + char *data = static_cast(_alloca(length)); + if (RegGetValue(hkey, NULL, "Path", RRF_RT_REG_SZ, NULL, data, &length) == + ERROR_SUCCESS) { + p = std::string(data); + res = true; + } } - return res; + RegCloseKey(hkey); + } + return res; } #endif -ConfigFile::ConfigFile() -{ - cfg = new config_t; - config_init(cfg); +ConfigFile::ConfigFile() { + cfg = new config_t; + config_init(cfg); } -ConfigFile::~ConfigFile() -{ - // delete cfg; +ConfigFile::~ConfigFile() { + // delete cfg; } +bool ConfigFile::locate() { + // temporary configuration -bool ConfigFile::locate() -{ - // temporary configuration + llvm::SmallString<128> p; + const char *filename = "ldc2.conf"; - llvm::SmallString<128> p; - const char* filename = "ldc2.conf"; +#define APPEND_FILENAME_AND_RETURN_IF_EXISTS \ + { \ + sys::path::append(p, filename); \ + if (sys::fs::exists(p.str())) { \ + pathstr = p.str(); \ + return true; \ + } \ + } -#define APPEND_FILENAME_AND_RETURN_IF_EXISTS \ - { \ - sys::path::append(p, filename); \ - if (sys::fs::exists(p.str())) \ - { \ - pathstr = p.str(); \ - return true; \ - } \ - } - - // try the current working dir - if (!sys::fs::current_path(p)) - APPEND_FILENAME_AND_RETURN_IF_EXISTS - - // try next to the executable - p = exe_path::getBinDir(); + // try the current working dir + if (!sys::fs::current_path(p)) APPEND_FILENAME_AND_RETURN_IF_EXISTS - // user configuration + // try next to the executable + p = exe_path::getBinDir(); + APPEND_FILENAME_AND_RETURN_IF_EXISTS - // try ~/.ldc - p = getUserHomeDirectory(); - sys::path::append(p, ".ldc"); - APPEND_FILENAME_AND_RETURN_IF_EXISTS + // user configuration + + // try ~/.ldc + p = getUserHomeDirectory(); + sys::path::append(p, ".ldc"); + APPEND_FILENAME_AND_RETURN_IF_EXISTS #if _WIN32 - // try home dir - p = getUserHomeDirectory(); - APPEND_FILENAME_AND_RETURN_IF_EXISTS + // try home dir + p = getUserHomeDirectory(); + APPEND_FILENAME_AND_RETURN_IF_EXISTS #endif - // system configuration + // system configuration - // try in etc relative to the executable: exe\..\etc - // do not use .. in path because of security risks - p = exe_path::getBaseDir(); - if (!p.empty()) - { - sys::path::append(p, "etc"); - APPEND_FILENAME_AND_RETURN_IF_EXISTS - } + // try in etc relative to the executable: exe\..\etc + // do not use .. in path because of security risks + p = exe_path::getBaseDir(); + if (!p.empty()) { + sys::path::append(p, "etc"); + APPEND_FILENAME_AND_RETURN_IF_EXISTS + } #if _WIN32 - // Try reading path from registry - if (ReadPathFromRegistry(p)) - { - sys::path::append(p, "etc"); - APPEND_FILENAME_AND_RETURN_IF_EXISTS - } + // Try reading path from registry + if (ReadPathFromRegistry(p)) { + sys::path::append(p, "etc"); + APPEND_FILENAME_AND_RETURN_IF_EXISTS + } #else - // try the install-prefix/etc - p = LDC_INSTALL_PREFIX; - sys::path::append(p, "etc"); - APPEND_FILENAME_AND_RETURN_IF_EXISTS + // try the install-prefix/etc + p = LDC_INSTALL_PREFIX; + sys::path::append(p, "etc"); + APPEND_FILENAME_AND_RETURN_IF_EXISTS - // try the install-prefix/etc/ldc - p = LDC_INSTALL_PREFIX; - sys::path::append(p, "etc"); - sys::path::append(p, "ldc"); - APPEND_FILENAME_AND_RETURN_IF_EXISTS + // try the install-prefix/etc/ldc + p = LDC_INSTALL_PREFIX; + sys::path::append(p, "etc"); + sys::path::append(p, "ldc"); + APPEND_FILENAME_AND_RETURN_IF_EXISTS - // try /etc (absolute path) - p = "/etc"; - APPEND_FILENAME_AND_RETURN_IF_EXISTS + // try /etc (absolute path) + p = "/etc"; + APPEND_FILENAME_AND_RETURN_IF_EXISTS - // try /etc/ldc (absolute path) - p = "/etc/ldc"; - APPEND_FILENAME_AND_RETURN_IF_EXISTS + // try /etc/ldc (absolute path) + p = "/etc/ldc"; + APPEND_FILENAME_AND_RETURN_IF_EXISTS #endif #undef APPEND_FILENAME_AND_RETURN_IF_EXISTS - fprintf(stderr, "Warning: failed to locate the configuration file %s\n", filename); + fprintf(stderr, "Warning: failed to locate the configuration file %s\n", + filename); + return false; +} + +bool ConfigFile::read(const char *explicitConfFile) { + // explicitly provided by user in command line? + if (explicitConfFile) { + const std::string clPath = explicitConfFile; + // treat an empty path (`-conf=`) as missing command-line option, + // defaulting to an auto-located config file, analogous to DMD + if (!clPath.empty()) { + if (sys::fs::exists(clPath)) + pathstr = clPath; + else + fprintf(stderr, "Warning: configuration file '%s' not found, falling " + "back to default\n", + clPath.c_str()); + } + } + + // locate file automatically if path is not set yet + if (pathstr.empty()) + if (!locate()) + return false; + + // read the cfg + if (!config_read_file(cfg, pathstr.c_str())) { + std::cerr << "error reading configuration file" << std::endl; return false; + } + + // make sure there's a default group + config_setting_t *root = config_lookup(cfg, "default"); + if (!root) { + std::cerr << "no default settings in configuration file" << std::endl; + return false; + } + if (!config_setting_is_group(root)) { + std::cerr << "default is not a group" << std::endl; + return false; + } + + // handle switches + if (config_setting_t *sw = config_setting_get_member(root, "switches")) { + // replace all %%ldcbinarypath%% occurrences by the path to the + // LDC bin directory (using forward slashes) + std::string binpathkey = "%%ldcbinarypath%%"; + + std::string binpath = exe_path::getBinDir(); + std::replace(binpath.begin(), binpath.end(), '\\', '/'); + + int len = config_setting_length(sw); + for (int i = 0; i < len; i++) { + std::string v(config_setting_get_string(config_setting_get_elem(sw, i))); + + size_t p; + while (std::string::npos != (p = v.find(binpathkey))) + v.replace(p, binpathkey.size(), binpath); + + switches.push_back(strdup(v.c_str())); + } + } + + return true; } - -bool ConfigFile::read(const char* explicitConfFile) -{ - // explicitly provided by user in command line? - if (explicitConfFile) - { - const std::string clPath = explicitConfFile; - // treat an empty path (`-conf=`) as missing command-line option, - // defaulting to an auto-located config file, analogous to DMD - if (!clPath.empty()) - { - if (sys::fs::exists(clPath)) - pathstr = clPath; - else - fprintf(stderr, "Warning: configuration file '%s' not found, falling back to default\n", clPath.c_str()); - } - } - - // locate file automatically if path is not set yet - if (pathstr.empty()) - if (!locate()) - return false; - - // read the cfg - if (!config_read_file(cfg, pathstr.c_str())) - { - std::cerr << "error reading configuration file" << std::endl; - return false; - } - - // make sure there's a default group - config_setting_t *root = config_lookup(cfg, "default"); - if (!root) - { - std::cerr << "no default settings in configuration file" << std::endl; - return false; - } - if (!config_setting_is_group(root)) - { - std::cerr << "default is not a group" << std::endl; - return false; - } - - // handle switches - if (config_setting_t *sw = config_setting_get_member(root, "switches")) - { - // replace all %%ldcbinarypath%% occurrences by the path to the - // LDC bin directory (using forward slashes) - std::string binpathkey = "%%ldcbinarypath%%"; - - std::string binpath = exe_path::getBinDir(); - std::replace(binpath.begin(), binpath.end(), '\\', '/'); - - int len = config_setting_length(sw); - for (int i = 0; i < len; i++) - { - std::string v(config_setting_get_string(config_setting_get_elem(sw, i))); - - size_t p; - while (std::string::npos != (p = v.find(binpathkey))) - v.replace(p, binpathkey.size(), binpath); - - switches.push_back(strdup(v.c_str())); - } - } - - return true; -} - diff --git a/driver/configfile.h b/driver/configfile.h index ae93b331d0..0b0d8c654f 100644 --- a/driver/configfile.h +++ b/driver/configfile.h @@ -19,30 +19,29 @@ #include "llvm/ADT/SmallString.h" #include "libconfig.h" -class ConfigFile -{ +class ConfigFile { public: - typedef std::vector s_vector; - typedef s_vector::iterator s_iterator; + typedef std::vector s_vector; + typedef s_vector::iterator s_iterator; public: - ConfigFile(); - ~ConfigFile(); + ConfigFile(); + ~ConfigFile(); - bool read(const char* explicitConfFile); + bool read(const char *explicitConfFile); - s_iterator switches_begin() { return switches.begin(); } - s_iterator switches_end() { return switches.end(); } + s_iterator switches_begin() { return switches.begin(); } + s_iterator switches_end() { return switches.end(); } - const std::string& path() { return pathstr; } + const std::string &path() { return pathstr; } private: - bool locate(); + bool locate(); - config_t* cfg; - std::string pathstr; + config_t *cfg; + std::string pathstr; - s_vector switches; + s_vector switches; }; #endif // LDC_DRIVER_CONFIGFILE_H diff --git a/driver/exe_path.cpp b/driver/exe_path.cpp index 90815ca86e..c94502480d 100644 --- a/driver/exe_path.cpp +++ b/driver/exe_path.cpp @@ -15,36 +15,33 @@ using std::string; namespace path = llvm::sys::path; -namespace { string exePath; } - -void exe_path::initialize(const char* arg0, void* mainAddress) -{ - assert(exePath.empty()); - exePath = llvm::sys::fs::getMainExecutable(arg0, mainAddress); +namespace { +string exePath; } -const string& exe_path::getExePath() -{ - assert(!exePath.empty()); - return exePath; +void exe_path::initialize(const char *arg0, void *mainAddress) { + assert(exePath.empty()); + exePath = llvm::sys::fs::getMainExecutable(arg0, mainAddress); } -string exe_path::getBinDir() -{ - assert(!exePath.empty()); - return path::parent_path(exePath); +const string &exe_path::getExePath() { + assert(!exePath.empty()); + return exePath; } -string exe_path::getBaseDir() -{ - string binDir = getBinDir(); - assert(!binDir.empty()); - return path::parent_path(binDir); +string exe_path::getBinDir() { + assert(!exePath.empty()); + return path::parent_path(exePath); } -string exe_path::prependBinDir(const char* suffix) -{ - llvm::SmallString<128> r(getBinDir()); - path::append(r, suffix); - return r.str(); +string exe_path::getBaseDir() { + string binDir = getBinDir(); + assert(!binDir.empty()); + return path::parent_path(binDir); +} + +string exe_path::prependBinDir(const char *suffix) { + llvm::SmallString<128> r(getBinDir()); + path::append(r, suffix); + return r.str(); } diff --git a/driver/exe_path.h b/driver/exe_path.h index a6dff7cf0b..cfb9dd4d5a 100644 --- a/driver/exe_path.h +++ b/driver/exe_path.h @@ -17,14 +17,13 @@ #include -namespace exe_path -{ - void initialize(const char* arg0, void* mainAddress); +namespace exe_path { +void initialize(const char *arg0, void *mainAddress); - const std::string& getExePath(); // /bin/ldc2 - std::string getBinDir(); // /bin - std::string getBaseDir(); // - std::string prependBinDir(const char* suffix); // /bin/ +const std::string &getExePath(); // /bin/ldc2 +std::string getBinDir(); // /bin +std::string getBaseDir(); // +std::string prependBinDir(const char *suffix); // /bin/ } #endif // LDC_DRIVER_EXE_PATH_H diff --git a/driver/ldc-version.h b/driver/ldc-version.h index 58adef26c5..6166e728a0 100644 --- a/driver/ldc-version.h +++ b/driver/ldc-version.h @@ -12,10 +12,9 @@ namespace ldc { -extern const char * const ldc_version; -extern const char * const dmd_version; -extern const char * const llvm_version; - +extern const char *const ldc_version; +extern const char *const dmd_version; +extern const char *const llvm_version; } #endif diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp index 43d55229ac..d2c59df302 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -45,7 +45,7 @@ //===----------------------------------------------------------------------===// #ifndef LDC_EXE_NAME -# error "Please define LDC_EXE_NAME to the name of the LDC executable to use." +#error "Please define LDC_EXE_NAME to the name of the LDC executable to use." #endif #include "driver/exe_path.h" @@ -70,7 +70,7 @@ #include #ifdef HAVE_SC_ARG_MAX -# include +#include #endif namespace ls = llvm::sys; @@ -83,77 +83,70 @@ void browse(const char *url); /** * Prints a formatted error message to stderr and exits the program. */ -void error(const char* fmt, ...) -{ - va_list argp; - va_start(argp, fmt); - fprintf(stderr, "Error: "); - vfprintf(stderr, fmt, argp); - fprintf(stderr, "\n"); - exit(EXIT_FAILURE); - va_end(argp); +void error(const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + fprintf(stderr, "Error: "); + vfprintf(stderr, fmt, argp); + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); + va_end(argp); } /** * Prints a formatted warning message to stderr. */ -void warning(const char* fmt, ...) -{ - va_list argp; - va_start(argp, fmt); - fprintf(stderr, "Warning: "); - vfprintf(stderr, fmt, argp); - fprintf(stderr, "\n"); - va_end(argp); +void warning(const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + fprintf(stderr, "Warning: "); + vfprintf(stderr, fmt, argp); + fprintf(stderr, "\n"); + va_end(argp); } -char* concat(const char* a, const char* b) -{ - size_t na = strlen(a); - size_t nb = strlen(b); - char* result = static_cast(malloc(na + nb + 1)); - assert(result); - memcpy(result, a, na); - memcpy(result + na, b, nb + 1); - return result; +char *concat(const char *a, const char *b) { + size_t na = strlen(a); + size_t nb = strlen(b); + char *result = static_cast(malloc(na + nb + 1)); + assert(result); + memcpy(result, a, na); + memcpy(result + na, b, nb + 1); + return result; } -char* concat(const char* a, int b) -{ - char bStr[14]; +char *concat(const char *a, int b) { + char bStr[14]; #if defined(_MSC_VER) - _snprintf_s(bStr, _countof(bStr), sizeof(bStr), "%d", b); + _snprintf_s(bStr, _countof(bStr), sizeof(bStr), "%d", b); #else - snprintf(bStr, sizeof(bStr), "%d", b); + snprintf(bStr, sizeof(bStr), "%d", b); #endif - return concat(a, bStr); + return concat(a, bStr); } /** * Runs the given executable, returning its error code. */ -int execute(const std::string &exePath, const char** args) -{ - std::string errorMsg; - int rc = ls::ExecuteAndWait(exePath, args, NULL, NULL, - 0, 0, &errorMsg); - if (!errorMsg.empty()) - { - error("Error executing %s: %s", exePath.c_str(), errorMsg.c_str()); - } - return rc; +int execute(const std::string &exePath, const char **args) { + std::string errorMsg; + int rc = ls::ExecuteAndWait(exePath, args, NULL, NULL, 0, 0, &errorMsg); + if (!errorMsg.empty()) { + error("Error executing %s: %s", exePath.c_str(), errorMsg.c_str()); + } + return rc; } /** * Prints usage information to stdout. */ -void printUsage(const char* argv0, const std::string &ldcPath) -{ - // Print version information by actually invoking ldc -version. - const char* args[] = { ldcPath.c_str(), "-version", NULL }; - execute(ldcPath, args); +void printUsage(const char *argv0, const std::string &ldcPath) { + // Print version information by actually invoking ldc -version. + const char *args[] = {ldcPath.c_str(), "-version", NULL}; + execute(ldcPath, args); - printf("\n\ + printf( + "\n\ Usage:\n\ %s files.d ... { -switch }\n\ \n\ @@ -198,7 +191,7 @@ Usage:\n\ #if 0 " -map generate linker .map file\n" #endif -" -boundscheck=[on|safeonly|off] bounds checks on, in @safe only, or off\n\ + " -boundscheck=[on|safeonly|off] bounds checks on, in @safe only, or off\n\ -noboundscheck no array bounds checking (deprecated, use -boundscheck=off)\n\ -O optimize\n\ -o- do not write object file\n\ @@ -208,7 +201,7 @@ Usage:\n\ #if 0 " -profile profile runtime performance of generated code\n" #endif -" -property enforce property syntax\n\ + " -property enforce property syntax\n\ -quiet suppress unnecessary messages\n\ -release compile release version\n\ -run srcfile args... run resulting program, passing args\n\ @@ -217,7 +210,7 @@ Usage:\n\ " -transition=id show additional info about language change identified by 'id'\n\ -transition=? list all language changes\n" #endif -" -unittest compile in unit tests\n\ + " -unittest compile in unit tests\n\ -v verbose\n\ -vcolumns print character (column) numbers in diagnostics\n\ -vdmd print the command used to invoke the underlying compiler\n\ @@ -226,13 +219,13 @@ Usage:\n\ #if 0 " -vtls list all variables going into thread local storage\n" #endif -" -vgc list all gc allocations including hidden ones\n\ + " -vgc list all gc allocations including hidden ones\n\ -verrors=num limit the number of error messages (0 means unlimited)\n\ -w enable warnings\n\ -wi enable informational warnings\n\ -X generate JSON file\n\ - -Xffilename write JSON file to filename\n\n", argv0 - ); + -Xffilename write JSON file to filename\n\n", + argv0); } /** @@ -242,772 +235,682 @@ Usage:\n\ * This corresponds to getenv_setargv() in DMD, but we need to duplicate it * here since it is defined in mars.c. */ -void appendEnvVar(const char* envVarName, std::vector& args) -{ - char *env = getenv(envVarName); - if (!env) - return; +void appendEnvVar(const char *envVarName, std::vector &args) { + char *env = getenv(envVarName); + if (!env) + return; - env = strdup(env); // create our own writable copy + env = strdup(env); // create our own writable copy - size_t j = 1; // leave argv[0] alone - while (1) - { - switch (*env) - { - case ' ': - case '\t': - env++; - break; + size_t j = 1; // leave argv[0] alone + while (1) { + switch (*env) { + case ' ': + case '\t': + env++; + break; - case 0: - return; + case 0: + return; - default: - args.push_back(env); // append - j++; - char* p = env; - int slash = 0; - int instring = 0; - char c = 0; + default: + args.push_back(env); // append + j++; + char *p = env; + int slash = 0; + int instring = 0; + char c = 0; - while (1) - { - c = *env++; - switch (c) - { - case '"': - p -= (slash >> 1); - if (slash & 1) - { p--; - goto Laddc; - } - instring ^= 1; - slash = 0; - continue; + while (1) { + c = *env++; + switch (c) { + case '"': + p -= (slash >> 1); + if (slash & 1) { + p--; + goto Laddc; + } + instring ^= 1; + slash = 0; + continue; - case ' ': - case '\t': - if (instring) - goto Laddc; - *p = 0; - break; + case ' ': + case '\t': + if (instring) + goto Laddc; + *p = 0; + break; - case '\\': - slash++; - *p++ = c; - continue; + case '\\': + slash++; + *p++ = c; + continue; - case 0: - *p = 0; - return; + case 0: + *p = 0; + return; - default: - Laddc: - slash = 0; - *p++ = c; - continue; - } - break; - } + default: + Laddc: + slash = 0; + *p++ = c; + continue; } + break; + } } + } } -struct BoundsCheck -{ - enum Type - { - defaultVal, - off, - safeOnly, - on - }; +struct BoundsCheck { + enum Type { defaultVal, off, safeOnly, on }; }; -struct Color -{ - enum Type - { - automatic, - on, - off - }; +struct Color { + enum Type { automatic, on, off }; }; -struct Debug -{ - enum Type - { - none, - normal, - pretendC - }; +struct Debug { + enum Type { none, normal, pretendC }; }; -struct Deprecated -{ - enum Type - { - allow, - warn, - error - }; +struct Deprecated { + enum Type { allow, warn, error }; }; -struct Model -{ - enum Type - { - automatic, - m32, - m64 - }; +struct Model { + enum Type { automatic, m32, m64 }; }; -struct Warnings -{ - enum Type - { - none, - asErrors, - informational - }; +struct Warnings { + enum Type { none, asErrors, informational }; }; -struct Params -{ - bool allinst; - Deprecated::Type useDeprecated; - bool compileOnly; - bool coverage; - bool emitSharedLib; - bool pic; - bool emitMap; - bool multiObj; - Debug::Type debugInfo; - bool alwaysStackFrame; - Model::Type targetModel; - bool profile; - bool verbose; - bool vcolumns; - bool vdmd; - bool vgc; - bool logTlsUse; - unsigned errorLimit; - bool errorLimitSet; - Warnings::Type warnings; - bool optimize; - bool noObj; - char* objDir; - char* objName; - bool preservePaths; - bool generateDocs; - char* docDir; - char* docName; - bool generateHeaders; - char* headerDir; - char* headerName; - bool generateJson; - char* jsonName; - bool ignoreUnsupportedPragmas; - bool enforcePropertySyntax; - bool enableInline; - bool emitStaticLib; - bool quiet; - bool release; - BoundsCheck::Type boundsChecks; - bool emitUnitTests; - std::vector modulePaths; - std::vector importPaths; - bool debugFlag; - unsigned debugLevel; - std::vector debugIdentifiers; - unsigned versionLevel; - std::vector versionIdentifiers; - std::vector linkerSwitches; - char* defaultLibName; - char* debugLibName; - char* moduleDepsFile; - Color::Type color; - bool useDIP25; - char* conf; +struct Params { + bool allinst; + Deprecated::Type useDeprecated; + bool compileOnly; + bool coverage; + bool emitSharedLib; + bool pic; + bool emitMap; + bool multiObj; + Debug::Type debugInfo; + bool alwaysStackFrame; + Model::Type targetModel; + bool profile; + bool verbose; + bool vcolumns; + bool vdmd; + bool vgc; + bool logTlsUse; + unsigned errorLimit; + bool errorLimitSet; + Warnings::Type warnings; + bool optimize; + bool noObj; + char *objDir; + char *objName; + bool preservePaths; + bool generateDocs; + char *docDir; + char *docName; + bool generateHeaders; + char *headerDir; + char *headerName; + bool generateJson; + char *jsonName; + bool ignoreUnsupportedPragmas; + bool enforcePropertySyntax; + bool enableInline; + bool emitStaticLib; + bool quiet; + bool release; + BoundsCheck::Type boundsChecks; + bool emitUnitTests; + std::vector modulePaths; + std::vector importPaths; + bool debugFlag; + unsigned debugLevel; + std::vector debugIdentifiers; + unsigned versionLevel; + std::vector versionIdentifiers; + std::vector linkerSwitches; + char *defaultLibName; + char *debugLibName; + char *moduleDepsFile; + Color::Type color; + bool useDIP25; + char *conf; - bool hiddenDebugB; - bool hiddenDebugC; - bool hiddenDebugF; - bool hiddenDebugR; - bool hiddenDebugX; - bool hiddenDebugY; + bool hiddenDebugB; + bool hiddenDebugC; + bool hiddenDebugF; + bool hiddenDebugR; + bool hiddenDebugX; + bool hiddenDebugY; - std::vector unknownSwitches; + std::vector unknownSwitches; - bool run; - std::vector files; - std::vector runArgs; + bool run; + std::vector files; + std::vector runArgs; Params() - : - allinst(false), - useDeprecated(Deprecated::warn), - compileOnly(false), - coverage(false), - emitSharedLib(false), - pic(false), - emitMap(false), - multiObj(false), - debugInfo(Debug::none), - alwaysStackFrame(false), - targetModel(Model::automatic), - profile(false), - verbose(false), - vcolumns(false), - vdmd(false), - vgc(false), - logTlsUse(false), - errorLimit(0), - errorLimitSet(false), - warnings(Warnings::none), - optimize(false), - noObj(false), - objDir(0), - objName(0), - preservePaths(false), - generateDocs(false), - docDir(0), - docName(0), - generateHeaders(false), - headerDir(0), - headerName(0), - generateJson(false), - jsonName(0), - ignoreUnsupportedPragmas(false), - enforcePropertySyntax(false), - enableInline(false), - emitStaticLib(false), - quiet(false), - release(false), - boundsChecks(BoundsCheck::defaultVal), - emitUnitTests(false), - debugFlag(false), - debugLevel(0), - versionLevel(0), - defaultLibName(0), - debugLibName(0), - moduleDepsFile(0), - color(Color::automatic), - useDIP25(false), - conf(0), - hiddenDebugB(false), - hiddenDebugC(false), - hiddenDebugF(false), - hiddenDebugR(false), - hiddenDebugX(false), - hiddenDebugY(false), - run(false) - { } + : allinst(false), useDeprecated(Deprecated::warn), compileOnly(false), + coverage(false), emitSharedLib(false), pic(false), emitMap(false), + multiObj(false), debugInfo(Debug::none), alwaysStackFrame(false), + targetModel(Model::automatic), profile(false), verbose(false), + vcolumns(false), vdmd(false), vgc(false), logTlsUse(false), + errorLimit(0), errorLimitSet(false), warnings(Warnings::none), + optimize(false), noObj(false), objDir(0), objName(0), + preservePaths(false), generateDocs(false), docDir(0), docName(0), + generateHeaders(false), headerDir(0), headerName(0), + generateJson(false), jsonName(0), ignoreUnsupportedPragmas(false), + enforcePropertySyntax(false), enableInline(false), emitStaticLib(false), + quiet(false), release(false), boundsChecks(BoundsCheck::defaultVal), + emitUnitTests(false), debugFlag(false), debugLevel(0), versionLevel(0), + defaultLibName(0), debugLibName(0), moduleDepsFile(0), + color(Color::automatic), useDIP25(false), conf(0), hiddenDebugB(false), + hiddenDebugC(false), hiddenDebugF(false), hiddenDebugR(false), + hiddenDebugX(false), hiddenDebugY(false), run(false) {} }; /** * Parses the flags from the given command line and the DFLAGS environment * variable into a Params struct. */ -Params parseArgs(size_t originalArgc, char** originalArgv, const std::string &ldcPath) -{ - // Expand any response files present into the list of arguments. - size_t argc = originalArgc; - char** argv = originalArgv; - if (response_expand(&argc, &argv)) - { - error("Could not read response file."); - } +Params parseArgs(size_t originalArgc, char **originalArgv, + const std::string &ldcPath) { + // Expand any response files present into the list of arguments. + size_t argc = originalArgc; + char **argv = originalArgv; + if (response_expand(&argc, &argv)) { + error("Could not read response file."); + } - std::vector args(argv, argv + argc); + std::vector args(argv, argv + argc); - appendEnvVar("DFLAGS", args); + appendEnvVar("DFLAGS", args); - Params result = Params(); - for (size_t i = 1; i < args.size(); i++) - { - char* p = args[i]; - if (*p == '-') - { - if (strcmp(p + 1, "allinst") == 0) - result.allinst = true; - else if (strcmp(p + 1, "de") == 0) - result.useDeprecated = Deprecated::error; - else if (strcmp(p + 1, "d") == 0) - result.useDeprecated = Deprecated::allow; - else if (strcmp(p + 1, "dw") == 0) - result.useDeprecated = Deprecated::warn; - else if (strcmp(p + 1, "c") == 0) - result.compileOnly = true; - else if (strncmp(p + 1, "color", 5) == 0) - { - result.color = Color::on; - // Parse: - // -color - // -color=on|off - if (p[6] == '=') - { - if (strcmp(p + 7, "off") == 0) - result.color = Color::off; - else if (strcmp(p + 7, "on") != 0) - goto Lerror; - } - else if (p[6]) - goto Lerror; - } - else if (strncmp(p + 1, "conf=", 5) == 0) - result.conf = p + 1 + 5; - else if (strcmp(p + 1, "cov") == 0) - // For "-cov=...", the whole cmdline switch is forwarded to LDC. - // For plain "-cov", the cmdline switch must be explicitly forwarded - // and result.coverage must be set to true to that effect. - result.coverage = (p[4] != '='); - else if (strcmp(p + 1, "dip25") == 0) - result.useDIP25 = true; - else if (strcmp(p + 1, "shared") == 0 - // backwards compatibility with old switch - || strcmp(p + 1, "dylib") == 0 - ) - result.emitSharedLib = true; - else if (strcmp(p + 1, "fPIC") == 0) - result.pic = true; - else if (strcmp(p + 1, "map") == 0) - result.emitMap = true; - else if (strcmp(p + 1, "multiobj") == 0) - result.multiObj = true; - else if (strcmp(p + 1, "g") == 0) - result.debugInfo = Debug::normal; - else if (strcmp(p + 1, "gc") == 0) - result.debugInfo = Debug::pretendC; - else if (strcmp(p + 1, "gs") == 0) - result.alwaysStackFrame = true; - else if (strcmp(p + 1, "gt") == 0) - error("use -profile instead of -gt\n"); - else if (strcmp(p + 1, "m32") == 0) - result.targetModel = Model::m32; - else if (strcmp(p + 1, "m64") == 0) - result.targetModel = Model::m64; - else if (strcmp(p + 1, "profile") == 0) - result.profile = true; - else if (memcmp(p + 1, "transition", 10) == 0) - warning("-transition not yet supported by LDC."); - else if (strcmp(p + 1, "v") == 0) - result.verbose = true; - else if (strcmp(p + 1, "vcolumns") == 0) - result.vcolumns = true; - else if (strcmp(p + 1, "vdmd") == 0) - result.vdmd = true; - else if (strcmp(p + 1, "vgc") == 0) - result.vgc = true; - else if (strcmp(p + 1, "vtls") == 0) - result.logTlsUse = true; - else if (strcmp(p + 1, "v1") == 0) - { - error("use DMD 1.0 series compilers for -v1 switch"); - break; - } - else if (memcmp(p + 1, "verrors", 7) == 0) - { - if (p[8] == '=' && isdigit((unsigned char)p[9])) - { - long num; - char* endp; - errno = 0; - num = strtol(p + 9, &endp, 10); - if (*endp || errno || num > INT_MAX) - goto Lerror; - // Bugzilla issue number - result.errorLimit = (unsigned) num; - result.errorLimitSet = true; - } - else - goto Lerror; - } - else if (strcmp(p + 1, "w") == 0) - result.warnings = Warnings::asErrors; - else if (strcmp(p + 1, "wi") == 0) - result.warnings = Warnings::informational; - else if (strcmp(p + 1, "O") == 0) - result.optimize = true; - else if (p[1] == 'o') - { - switch (p[2]) - { - case '-': - result.noObj = true; - break; + Params result = Params(); + for (size_t i = 1; i < args.size(); i++) { + char *p = args[i]; + if (*p == '-') { + if (strcmp(p + 1, "allinst") == 0) + result.allinst = true; + else if (strcmp(p + 1, "de") == 0) + result.useDeprecated = Deprecated::error; + else if (strcmp(p + 1, "d") == 0) + result.useDeprecated = Deprecated::allow; + else if (strcmp(p + 1, "dw") == 0) + result.useDeprecated = Deprecated::warn; + else if (strcmp(p + 1, "c") == 0) + result.compileOnly = true; + else if (strncmp(p + 1, "color", 5) == 0) { + result.color = Color::on; + // Parse: + // -color + // -color=on|off + if (p[6] == '=') { + if (strcmp(p + 7, "off") == 0) + result.color = Color::off; + else if (strcmp(p + 7, "on") != 0) + goto Lerror; + } else if (p[6]) + goto Lerror; + } else if (strncmp(p + 1, "conf=", 5) == 0) + result.conf = p + 1 + 5; + else if (strcmp(p + 1, "cov") == 0) + // For "-cov=...", the whole cmdline switch is forwarded to LDC. + // For plain "-cov", the cmdline switch must be explicitly forwarded + // and result.coverage must be set to true to that effect. + result.coverage = (p[4] != '='); + else if (strcmp(p + 1, "dip25") == 0) + result.useDIP25 = true; + else if (strcmp(p + 1, "shared") == 0 + // backwards compatibility with old switch + || strcmp(p + 1, "dylib") == 0) + result.emitSharedLib = true; + else if (strcmp(p + 1, "fPIC") == 0) + result.pic = true; + else if (strcmp(p + 1, "map") == 0) + result.emitMap = true; + else if (strcmp(p + 1, "multiobj") == 0) + result.multiObj = true; + else if (strcmp(p + 1, "g") == 0) + result.debugInfo = Debug::normal; + else if (strcmp(p + 1, "gc") == 0) + result.debugInfo = Debug::pretendC; + else if (strcmp(p + 1, "gs") == 0) + result.alwaysStackFrame = true; + else if (strcmp(p + 1, "gt") == 0) + error("use -profile instead of -gt\n"); + else if (strcmp(p + 1, "m32") == 0) + result.targetModel = Model::m32; + else if (strcmp(p + 1, "m64") == 0) + result.targetModel = Model::m64; + else if (strcmp(p + 1, "profile") == 0) + result.profile = true; + else if (memcmp(p + 1, "transition", 10) == 0) + warning("-transition not yet supported by LDC."); + else if (strcmp(p + 1, "v") == 0) + result.verbose = true; + else if (strcmp(p + 1, "vcolumns") == 0) + result.vcolumns = true; + else if (strcmp(p + 1, "vdmd") == 0) + result.vdmd = true; + else if (strcmp(p + 1, "vgc") == 0) + result.vgc = true; + else if (strcmp(p + 1, "vtls") == 0) + result.logTlsUse = true; + else if (strcmp(p + 1, "v1") == 0) { + error("use DMD 1.0 series compilers for -v1 switch"); + break; + } else if (memcmp(p + 1, "verrors", 7) == 0) { + if (p[8] == '=' && isdigit((unsigned char)p[9])) { + long num; + char *endp; + errno = 0; + num = strtol(p + 9, &endp, 10); + if (*endp || errno || num > INT_MAX) + goto Lerror; + // Bugzilla issue number + result.errorLimit = (unsigned)num; + result.errorLimitSet = true; + } else + goto Lerror; + } else if (strcmp(p + 1, "w") == 0) + result.warnings = Warnings::asErrors; + else if (strcmp(p + 1, "wi") == 0) + result.warnings = Warnings::informational; + else if (strcmp(p + 1, "O") == 0) + result.optimize = true; + else if (p[1] == 'o') { + switch (p[2]) { + case '-': + result.noObj = true; + break; - case 'd': - if (!p[3]) - goto Lnoarg; - result.objDir = p + 3; - break; + case 'd': + if (!p[3]) + goto Lnoarg; + result.objDir = p + 3; + break; - case 'f': - if (!p[3]) - goto Lnoarg; - result.objName = p + 3; - break; + case 'f': + if (!p[3]) + goto Lnoarg; + result.objName = p + 3; + break; - case 'p': - if (p[3]) - goto Lerror; - result.preservePaths = 1; - break; + case 'p': + if (p[3]) + goto Lerror; + result.preservePaths = 1; + break; - case 0: - error("-o no longer supported, use -of or -od"); - break; + case 0: + error("-o no longer supported, use -of or -od"); + break; - default: - goto Lerror; - } - } - else if (p[1] == 'D') - { - result.generateDocs = true; - switch (p[2]) - { - case 'd': - if (!p[3]) - goto Lnoarg; - result.docDir = p + 3; - break; - case 'f': - if (!p[3]) - goto Lnoarg; - result.docName = p + 3; - break; - - case 0: - break; - - default: - goto Lerror; - } - } - else if (p[1] == 'H') - { - result.generateHeaders = true; - switch (p[2]) - { - case 'd': - if (!p[3]) - goto Lnoarg; - result.headerDir = p + 3; - break; - - case 'f': - if (!p[3]) - goto Lnoarg; - result.headerName = p + 3; - break; - - case 0: - break; - - default: - goto Lerror; - } - } - else if (p[1] == 'X') - { - result.generateJson = true; - switch (p[2]) - { - case 'f': - if (!p[3]) - goto Lnoarg; - result.jsonName = p + 3; - break; - - case 0: - break; - - default: - goto Lerror; - } - } - else if (strcmp(p + 1, "ignore") == 0) - result.ignoreUnsupportedPragmas = true; - else if (strcmp(p + 1, "property") == 0) - result.enforcePropertySyntax = true; - else if (strcmp(p + 1, "inline") == 0) - result.enableInline = true; - else if (strcmp(p + 1, "lib") == 0) - result.emitStaticLib = true; - else if (strcmp(p + 1, "quiet") == 0) - result.quiet = 1; - else if (strcmp(p + 1, "release") == 0) - result.release = 1; - else if (strcmp(p + 1, "noboundscheck") == 0) - result.boundsChecks = BoundsCheck::off; - else if (memcmp(p + 1, "boundscheck", 11) == 0) - { - if (p[12] == '=') - { - if (strcmp(p + 13, "on") == 0) - result.boundsChecks = BoundsCheck::on; - else if (strcmp(p + 13, "safeonly") == 0) - result.boundsChecks = BoundsCheck::safeOnly; - else if (strcmp(p + 13, "off") == 0) - result.boundsChecks = BoundsCheck::off; - else - goto Lerror; - } - } - else if (strcmp(p + 1, "unittest") == 0) - result.emitUnitTests = 1; - else if (p[1] == 'I') - result.modulePaths.push_back(p + 2); - else if (p[1] == 'J') - result.importPaths.push_back(p + 2); - else if (memcmp(p + 1, "debug", 5) == 0 && p[6] != 'l') - { - // Parse: - // -debug - // -debug=number - // -debug=identifier - if (p[6] == '=') - { - if (isdigit((unsigned char)p[7])) - { long level; - - errno = 0; - level = strtol(p + 7, &p, 10); - if (*p || errno || level > INT_MAX) - goto Lerror; - result.debugLevel = (int)level; - } - else - result.debugIdentifiers.push_back(p + 7); - } - else if (p[6]) - goto Lerror; - else - result.debugFlag = true; - } - else if (memcmp(p + 1, "version", 5) == 0) - { - // Parse: - // -version=number - // -version=identifier - if (p[8] == '=') - { - if (isdigit((unsigned char)p[9])) - { long level; - - errno = 0; - level = strtol(p + 9, &p, 10); - if (*p || errno || level > INT_MAX) - goto Lerror; - result.versionLevel = (int)level; - } - else - result.versionIdentifiers.push_back(p + 9); - } - else - goto Lerror; - } - else if (strcmp(p + 1, "-b") == 0) - result.hiddenDebugB = 1; - else if (strcmp(p + 1, "-c") == 0) - result.hiddenDebugC = 1; - else if (strcmp(p + 1, "-f") == 0) - result.hiddenDebugF = 1; - else if (strcmp(p + 1, "-help") == 0) - { - printUsage(originalArgv[0], ldcPath); - exit(EXIT_SUCCESS); - } - else if (strcmp(p + 1, "-r") == 0) - result.hiddenDebugR = 1; - else if (strcmp(p + 1, "-x") == 0) - result.hiddenDebugX = 1; - else if (strcmp(p + 1, "-y") == 0) - result.hiddenDebugY = 1; - else if (p[1] == 'L') - { - result.linkerSwitches.push_back(p + 2); - } - else if (memcmp(p + 1, "defaultlib=", 11) == 0) - { - result.defaultLibName = p + 1 + 11; - } - else if (memcmp(p + 1, "debuglib=", 9) == 0) - { - result.debugLibName = p + 1 + 9; - } - else if (memcmp(p + 1, "deps=", 5) == 0) - { - result.moduleDepsFile = p + 1 + 5; - if (!result.moduleDepsFile[0]) - goto Lnoarg; - } - else if (memcmp(p + 1, "man", 3) == 0) - { - browse("http://wiki.dlang.org/LDC"); - exit(EXIT_SUCCESS); - } - else if (strcmp(p + 1, "run") == 0) - { - result.run = true; - int runargCount = ((i >= originalArgc) ? argc : originalArgc) - i - 1; - if (runargCount) - { - result.files.push_back(argv[i + 1]); - result.runArgs = std::vector(argv + i + 2, argv + i + runargCount + 1); - i += runargCount; - } - else - { - result.run = false; - goto Lnoarg; - } - } - else if (p[1] == 'C') - { - result.unknownSwitches.push_back(concat("-", p + 2)); - } - else - { - Lerror: - result.unknownSwitches.push_back(p); - continue; - - Lnoarg: - error("argument expected for switch '%s'", p); - continue; - } + default: + goto Lerror; } + } else if (p[1] == 'D') { + result.generateDocs = true; + switch (p[2]) { + case 'd': + if (!p[3]) + goto Lnoarg; + result.docDir = p + 3; + break; + case 'f': + if (!p[3]) + goto Lnoarg; + result.docName = p + 3; + break; + + case 0: + break; + + default: + goto Lerror; + } + } else if (p[1] == 'H') { + result.generateHeaders = true; + switch (p[2]) { + case 'd': + if (!p[3]) + goto Lnoarg; + result.headerDir = p + 3; + break; + + case 'f': + if (!p[3]) + goto Lnoarg; + result.headerName = p + 3; + break; + + case 0: + break; + + default: + goto Lerror; + } + } else if (p[1] == 'X') { + result.generateJson = true; + switch (p[2]) { + case 'f': + if (!p[3]) + goto Lnoarg; + result.jsonName = p + 3; + break; + + case 0: + break; + + default: + goto Lerror; + } + } else if (strcmp(p + 1, "ignore") == 0) + result.ignoreUnsupportedPragmas = true; + else if (strcmp(p + 1, "property") == 0) + result.enforcePropertySyntax = true; + else if (strcmp(p + 1, "inline") == 0) + result.enableInline = true; + else if (strcmp(p + 1, "lib") == 0) + result.emitStaticLib = true; + else if (strcmp(p + 1, "quiet") == 0) + result.quiet = 1; + else if (strcmp(p + 1, "release") == 0) + result.release = 1; + else if (strcmp(p + 1, "noboundscheck") == 0) + result.boundsChecks = BoundsCheck::off; + else if (memcmp(p + 1, "boundscheck", 11) == 0) { + if (p[12] == '=') { + if (strcmp(p + 13, "on") == 0) + result.boundsChecks = BoundsCheck::on; + else if (strcmp(p + 13, "safeonly") == 0) + result.boundsChecks = BoundsCheck::safeOnly; + else if (strcmp(p + 13, "off") == 0) + result.boundsChecks = BoundsCheck::off; + else + goto Lerror; + } + } else if (strcmp(p + 1, "unittest") == 0) + result.emitUnitTests = 1; + else if (p[1] == 'I') + result.modulePaths.push_back(p + 2); + else if (p[1] == 'J') + result.importPaths.push_back(p + 2); + else if (memcmp(p + 1, "debug", 5) == 0 && p[6] != 'l') { + // Parse: + // -debug + // -debug=number + // -debug=identifier + if (p[6] == '=') { + if (isdigit((unsigned char)p[7])) { + long level; + + errno = 0; + level = strtol(p + 7, &p, 10); + if (*p || errno || level > INT_MAX) + goto Lerror; + result.debugLevel = (int)level; + } else + result.debugIdentifiers.push_back(p + 7); + } else if (p[6]) + goto Lerror; else - { -// FIXME: #if TARGET_WINDOS - llvm::StringRef ext = ls::path::extension(p); - if (ext.equals_lower("exe")) - { - result.objName = p; - continue; - } -// #endif - result.files.push_back(p); - } - } - if (result.files.empty()) - { + result.debugFlag = true; + } else if (memcmp(p + 1, "version", 5) == 0) { + // Parse: + // -version=number + // -version=identifier + if (p[8] == '=') { + if (isdigit((unsigned char)p[9])) { + long level; + + errno = 0; + level = strtol(p + 9, &p, 10); + if (*p || errno || level > INT_MAX) + goto Lerror; + result.versionLevel = (int)level; + } else + result.versionIdentifiers.push_back(p + 9); + } else + goto Lerror; + } else if (strcmp(p + 1, "-b") == 0) + result.hiddenDebugB = 1; + else if (strcmp(p + 1, "-c") == 0) + result.hiddenDebugC = 1; + else if (strcmp(p + 1, "-f") == 0) + result.hiddenDebugF = 1; + else if (strcmp(p + 1, "-help") == 0) { printUsage(originalArgv[0], ldcPath); - error("No source file specified."); + exit(EXIT_SUCCESS); + } else if (strcmp(p + 1, "-r") == 0) + result.hiddenDebugR = 1; + else if (strcmp(p + 1, "-x") == 0) + result.hiddenDebugX = 1; + else if (strcmp(p + 1, "-y") == 0) + result.hiddenDebugY = 1; + else if (p[1] == 'L') { + result.linkerSwitches.push_back(p + 2); + } else if (memcmp(p + 1, "defaultlib=", 11) == 0) { + result.defaultLibName = p + 1 + 11; + } else if (memcmp(p + 1, "debuglib=", 9) == 0) { + result.debugLibName = p + 1 + 9; + } else if (memcmp(p + 1, "deps=", 5) == 0) { + result.moduleDepsFile = p + 1 + 5; + if (!result.moduleDepsFile[0]) + goto Lnoarg; + } else if (memcmp(p + 1, "man", 3) == 0) { + browse("http://wiki.dlang.org/LDC"); + exit(EXIT_SUCCESS); + } else if (strcmp(p + 1, "run") == 0) { + result.run = true; + int runargCount = ((i >= originalArgc) ? argc : originalArgc) - i - 1; + if (runargCount) { + result.files.push_back(argv[i + 1]); + result.runArgs = + std::vector(argv + i + 2, argv + i + runargCount + 1); + i += runargCount; + } else { + result.run = false; + goto Lnoarg; + } + } else if (p[1] == 'C') { + result.unknownSwitches.push_back(concat("-", p + 2)); + } else { + Lerror: + result.unknownSwitches.push_back(p); + continue; + + Lnoarg: + error("argument expected for switch '%s'", p); + continue; + } + } else { + // FIXME: #if TARGET_WINDOS + llvm::StringRef ext = ls::path::extension(p); + if (ext.equals_lower("exe")) { + result.objName = p; + continue; + } + // #endif + result.files.push_back(p); } - return result; + } + if (result.files.empty()) { + printUsage(originalArgv[0], ldcPath); + error("No source file specified."); + } + return result; } -void pushSwitches(const char* prefix, const std::vector& vals, std::vector& r) -{ - for (auto v : vals) - r.push_back(concat(prefix, v)); +void pushSwitches(const char *prefix, const std::vector &vals, + std::vector &r) { + for (auto v : vals) + r.push_back(concat(prefix, v)); } /** * Appends the LDC command line parameters corresponding to the given set of * parameters to r. */ -void buildCommandLine(std::vector& r, const Params& p) -{ - if (p.allinst) r.push_back("-allinst"); - if (p.useDeprecated == Deprecated::allow) r.push_back("-d"); - if (p.useDeprecated == Deprecated::error) r.push_back("-de"); - if (p.compileOnly) r.push_back("-c"); - if (p.coverage) r.push_back("-cov"); - if (p.emitSharedLib) r.push_back("-shared"); - if (p.pic) r.push_back("-relocation-model=pic"); - if (p.emitMap) warning("Map file generation not yet supported by LDC."); - if (!p.emitStaticLib && ((!p.multiObj && !p.compileOnly) || p.objName)) - r.push_back("-singleobj"); - if (p.debugInfo == Debug::normal) r.push_back("-g"); - else if (p.debugInfo == Debug::pretendC) r.push_back("-gc"); - if (p.alwaysStackFrame) r.push_back("-disable-fp-elim"); - if (p.targetModel == Model::m32) r.push_back("-m32"); - else if (p.targetModel == Model::m64) r.push_back("-m64"); - if (p.profile) warning("CPU profile generation not yet supported by LDC."); - if (p.verbose) r.push_back("-v"); - if (p.vcolumns) r.push_back("-vcolumns"); - if (p.vgc) r.push_back("-vgc"); - if (p.logTlsUse) warning("-vtls not yet supported by LDC."); - if (p.errorLimitSet) r.push_back(concat("-verrors=", p.errorLimit)); - if (p.warnings == Warnings::asErrors) r.push_back("-w"); - else if (p.warnings == Warnings::informational) r.push_back("-wi"); - if (p.optimize) r.push_back("-O3"); - if (p.noObj) r.push_back("-o-"); - if (p.objDir) r.push_back(concat("-od=", p.objDir)); - if (p.objName) r.push_back(concat("-of=", p.objName)); - if (p.preservePaths) r.push_back("-op"); - if (p.generateDocs) r.push_back("-D"); - if (p.docDir) r.push_back(concat("-Dd=", p.docDir)); - if (p.docName) r.push_back(concat("-Df=", p.docName)); - if (p.generateHeaders) r.push_back("-H"); - if (p.headerDir) r.push_back(concat("-Hd=", p.headerDir)); - if (p.headerName) r.push_back(concat("-Hf=", p.headerName)); - if (p.generateJson) r.push_back("-X"); - if (p.jsonName) r.push_back(concat("-Xf=", p.jsonName)); - if (p.ignoreUnsupportedPragmas) r.push_back("-ignore"); - if (p.enforcePropertySyntax) r.push_back("-property"); - if (p.enableInline) { - // -inline also influences .di generation with DMD. - r.push_back("-enable-inlining"); - r.push_back("-Hkeep-all-bodies"); - } - if (p.emitStaticLib) r.push_back("-lib"); - // -quiet is the default in (newer?) frontend versions, just ignore it. - if (p.release) r.push_back("-release"); // Also disables boundscheck. - if (p.boundsChecks == BoundsCheck::on) r.push_back("-boundscheck=on"); - if (p.boundsChecks == BoundsCheck::safeOnly) r.push_back("-boundscheck=safeonly"); - if (p.boundsChecks == BoundsCheck::off) r.push_back("-boundscheck=off"); - if (p.emitUnitTests) r.push_back("-unittest"); - pushSwitches("-I=", p.modulePaths, r); - pushSwitches("-J=", p.importPaths, r); - if (p.debugFlag) r.push_back("-d-debug"); - if (p.debugLevel) r.push_back(concat("-d-debug=", p.debugLevel)); - pushSwitches("-d-debug=", p.debugIdentifiers, r); - if (p.versionLevel) r.push_back(concat("-d-version=", p.versionLevel)); - pushSwitches("-d-version=", p.versionIdentifiers, r); - pushSwitches("-L=", p.linkerSwitches, r); - if (p.defaultLibName) r.push_back(concat("-defaultlib=", p.defaultLibName)); - if (p.debugLibName) r.push_back(concat("-debuglib=", p.debugLibName)); - if (p.moduleDepsFile) r.push_back(concat("-deps=", p.moduleDepsFile)); - if (p.color == Color::on) r.push_back("-enable-color"); - if (p.color == Color::off) r.push_back("-disable-color"); - if (p.useDIP25) r.push_back("-dip25"); - if (p.conf) r.push_back(concat("-conf=", p.conf)); - if (p.hiddenDebugB) r.push_back("-hidden-debug-b"); - if (p.hiddenDebugC) r.push_back("-hidden-debug-c"); - if (p.hiddenDebugF) r.push_back("-hidden-debug-f"); - if (p.hiddenDebugR) r.push_back("-hidden-debug-r"); - if (p.hiddenDebugX) r.push_back("-hidden-debug-x"); - if (p.hiddenDebugY) r.push_back("-hidden-debug-y"); - r.insert(r.end(), p.unknownSwitches.begin(), p.unknownSwitches.end()); - if (p.run) r.push_back("-run"); - r.insert(r.end(), p.files.begin(), p.files.end()); - r.insert(r.end(), p.runArgs.begin(), p.runArgs.end()); +void buildCommandLine(std::vector &r, const Params &p) { + if (p.allinst) + r.push_back("-allinst"); + if (p.useDeprecated == Deprecated::allow) + r.push_back("-d"); + if (p.useDeprecated == Deprecated::error) + r.push_back("-de"); + if (p.compileOnly) + r.push_back("-c"); + if (p.coverage) + r.push_back("-cov"); + if (p.emitSharedLib) + r.push_back("-shared"); + if (p.pic) + r.push_back("-relocation-model=pic"); + if (p.emitMap) + warning("Map file generation not yet supported by LDC."); + if (!p.emitStaticLib && ((!p.multiObj && !p.compileOnly) || p.objName)) + r.push_back("-singleobj"); + if (p.debugInfo == Debug::normal) + r.push_back("-g"); + else if (p.debugInfo == Debug::pretendC) + r.push_back("-gc"); + if (p.alwaysStackFrame) + r.push_back("-disable-fp-elim"); + if (p.targetModel == Model::m32) + r.push_back("-m32"); + else if (p.targetModel == Model::m64) + r.push_back("-m64"); + if (p.profile) + warning("CPU profile generation not yet supported by LDC."); + if (p.verbose) + r.push_back("-v"); + if (p.vcolumns) + r.push_back("-vcolumns"); + if (p.vgc) + r.push_back("-vgc"); + if (p.logTlsUse) + warning("-vtls not yet supported by LDC."); + if (p.errorLimitSet) + r.push_back(concat("-verrors=", p.errorLimit)); + if (p.warnings == Warnings::asErrors) + r.push_back("-w"); + else if (p.warnings == Warnings::informational) + r.push_back("-wi"); + if (p.optimize) + r.push_back("-O3"); + if (p.noObj) + r.push_back("-o-"); + if (p.objDir) + r.push_back(concat("-od=", p.objDir)); + if (p.objName) + r.push_back(concat("-of=", p.objName)); + if (p.preservePaths) + r.push_back("-op"); + if (p.generateDocs) + r.push_back("-D"); + if (p.docDir) + r.push_back(concat("-Dd=", p.docDir)); + if (p.docName) + r.push_back(concat("-Df=", p.docName)); + if (p.generateHeaders) + r.push_back("-H"); + if (p.headerDir) + r.push_back(concat("-Hd=", p.headerDir)); + if (p.headerName) + r.push_back(concat("-Hf=", p.headerName)); + if (p.generateJson) + r.push_back("-X"); + if (p.jsonName) + r.push_back(concat("-Xf=", p.jsonName)); + if (p.ignoreUnsupportedPragmas) + r.push_back("-ignore"); + if (p.enforcePropertySyntax) + r.push_back("-property"); + if (p.enableInline) { + // -inline also influences .di generation with DMD. + r.push_back("-enable-inlining"); + r.push_back("-Hkeep-all-bodies"); + } + if (p.emitStaticLib) + r.push_back("-lib"); + // -quiet is the default in (newer?) frontend versions, just ignore it. + if (p.release) + r.push_back("-release"); // Also disables boundscheck. + if (p.boundsChecks == BoundsCheck::on) + r.push_back("-boundscheck=on"); + if (p.boundsChecks == BoundsCheck::safeOnly) + r.push_back("-boundscheck=safeonly"); + if (p.boundsChecks == BoundsCheck::off) + r.push_back("-boundscheck=off"); + if (p.emitUnitTests) + r.push_back("-unittest"); + pushSwitches("-I=", p.modulePaths, r); + pushSwitches("-J=", p.importPaths, r); + if (p.debugFlag) + r.push_back("-d-debug"); + if (p.debugLevel) + r.push_back(concat("-d-debug=", p.debugLevel)); + pushSwitches("-d-debug=", p.debugIdentifiers, r); + if (p.versionLevel) + r.push_back(concat("-d-version=", p.versionLevel)); + pushSwitches("-d-version=", p.versionIdentifiers, r); + pushSwitches("-L=", p.linkerSwitches, r); + if (p.defaultLibName) + r.push_back(concat("-defaultlib=", p.defaultLibName)); + if (p.debugLibName) + r.push_back(concat("-debuglib=", p.debugLibName)); + if (p.moduleDepsFile) + r.push_back(concat("-deps=", p.moduleDepsFile)); + if (p.color == Color::on) + r.push_back("-enable-color"); + if (p.color == Color::off) + r.push_back("-disable-color"); + if (p.useDIP25) + r.push_back("-dip25"); + if (p.conf) + r.push_back(concat("-conf=", p.conf)); + if (p.hiddenDebugB) + r.push_back("-hidden-debug-b"); + if (p.hiddenDebugC) + r.push_back("-hidden-debug-c"); + if (p.hiddenDebugF) + r.push_back("-hidden-debug-f"); + if (p.hiddenDebugR) + r.push_back("-hidden-debug-r"); + if (p.hiddenDebugX) + r.push_back("-hidden-debug-x"); + if (p.hiddenDebugY) + r.push_back("-hidden-debug-y"); + r.insert(r.end(), p.unknownSwitches.begin(), p.unknownSwitches.end()); + if (p.run) + r.push_back("-run"); + r.insert(r.end(), p.files.begin(), p.files.end()); + r.insert(r.end(), p.runArgs.begin(), p.runArgs.end()); } /** * Returns the OS-dependent length limit for the command line when invoking * subprocesses. */ -size_t maxCommandLineLen() -{ +size_t maxCommandLineLen() { #if defined(HAVE_SC_ARG_MAX) - // http://www.in-ulm.de/~mascheck/various/argmax – the factor 2 is just - // a wild guess to account for the enviroment. - return sysconf(_SC_ARG_MAX) / 2; + // http://www.in-ulm.de/~mascheck/various/argmax – the factor 2 is just + // a wild guess to account for the enviroment. + return sysconf(_SC_ARG_MAX) / 2; #elif defined(_WIN32) - // http://blogs.msdn.com/b/oldnewthing/archive/2003/12/10/56028.aspx - return 32767; + // http://blogs.msdn.com/b/oldnewthing/archive/2003/12/10/56028.aspx + return 32767; #else -# error "Do not know how to determine maximum command line length." +#error "Do not know how to determine maximum command line length." #endif } @@ -1016,115 +919,107 @@ size_t maxCommandLineLen() * nothing was found. Search paths: 1. Directory where this binary resides. * 2. System PATH. */ -std::string locateBinary(std::string exeName) -{ - std::string path = exe_path::prependBinDir(exeName.c_str()); - if (ls::fs::can_execute(path)) return path; +std::string locateBinary(std::string exeName) { + std::string path = exe_path::prependBinDir(exeName.c_str()); + if (ls::fs::can_execute(path)) + return path; #if LDC_LLVM_VER >= 306 - llvm::ErrorOr res = ls::findProgramByName(exeName); - path = res ? res.get() : std::string(); + llvm::ErrorOr res = ls::findProgramByName(exeName); + path = res ? res.get() : std::string(); #else - path = ls::FindProgramByName(exeName); + path = ls::FindProgramByName(exeName); #endif - if (ls::fs::can_execute(path)) return path; + if (ls::fs::can_execute(path)) + return path; - return ""; + return ""; } /** * Makes sure the given directory (absolute or relative) exists on disk. */ -static void createOutputDir(const char* dir) { - if (ls::fs::create_directories(dir)) - error("Could not create output directory '%s'.", dir); +static void createOutputDir(const char *dir) { + if (ls::fs::create_directories(dir)) + error("Could not create output directory '%s'.", dir); } -static size_t addStrlen(size_t acc, const char* str) -{ - if (!str) return acc; - return acc + strlen(str); +static size_t addStrlen(size_t acc, const char *str) { + if (!str) + return acc; + return acc + strlen(str); } -int main(int argc, char *argv[]) -{ - exe_path::initialize(argv[0], reinterpret_cast(main)); +int main(int argc, char *argv[]) { + exe_path::initialize(argv[0], reinterpret_cast(main)); - std::string ldcExeName = LDC_EXE_NAME; + std::string ldcExeName = LDC_EXE_NAME; #ifdef _WIN32 - ldcExeName += ".exe"; + ldcExeName += ".exe"; #endif - std::string ldcPath = locateBinary(ldcExeName); - if (ldcPath.empty()) - { - error("Could not locate " LDC_EXE_NAME " executable."); + std::string ldcPath = locateBinary(ldcExeName); + if (ldcPath.empty()) { + error("Could not locate " LDC_EXE_NAME " executable."); + } + + // We need to manually set up argv[0] and the terminating NULL. + std::vector args; + args.push_back(ldcPath.c_str()); + + Params p = parseArgs(argc, argv, ldcPath); + buildCommandLine(args, p); + if (p.vdmd) { + printf(" -- Invoking:"); + for (size_t i = 0; i < args.size(); ++i) + printf(" %s", args[i]); + puts(""); + } + + args.push_back(NULL); + + // On Linux, DMD creates output directores that don't already exist, while + // LDC does not (and neither does GDC). Do this here for rdmd compatibility. + if (p.objName) { + llvm::SmallString<256> outputPath(p.objName); + ls::path::remove_filename(outputPath); + if (!outputPath.empty()) + createOutputDir(outputPath.c_str()); + } + + if (p.objDir) + createOutputDir(p.objDir); + + // Check if we need to write out a response file. + size_t totalLen = std::accumulate(args.begin(), args.end(), 0, addStrlen); + if (totalLen > maxCommandLineLen()) { + int rspFd; + llvm::SmallString<128> rspPath; + if (ls::fs::createUniqueFile("ldmd-%%-%%-%%-%%.rsp", rspFd, rspPath)) { + error("Could not open temporary response file."); } - // We need to manually set up argv[0] and the terminating NULL. - std::vector args; - args.push_back(ldcPath.c_str()); - - Params p = parseArgs(argc, argv, ldcPath); - buildCommandLine(args, p); - if (p.vdmd) { - printf(" -- Invoking:"); - for (size_t i = 0; i < args.size(); ++i) - printf(" %s", args[i]); - puts(""); + llvm::raw_fd_ostream rspOut(rspFd, /*shouldClose=*/true); + for (auto arg : args) + rspOut << arg << '\n'; } - args.push_back(NULL); + std::string rspArg = "@"; + rspArg += rspPath.str(); - // On Linux, DMD creates output directores that don't already exist, while - // LDC does not (and neither does GDC). Do this here for rdmd compatibility. - if (p.objName) - { - llvm::SmallString<256> outputPath(p.objName); - ls::path::remove_filename(outputPath); - if (!outputPath.empty()) - createOutputDir(outputPath.c_str()); + std::vector newArgs; + newArgs.push_back(argv[0]); + newArgs.push_back(rspArg.c_str()); + newArgs.push_back(NULL); + + int rc = execute(ldcPath, &newArgs[0]); + + if (ls::fs::remove(rspPath.str())) { + warning("Could not remove response file."); } - if (p.objDir) - createOutputDir(p.objDir); - - // Check if we need to write out a response file. - size_t totalLen = std::accumulate(args.begin(), args.end(), 0, addStrlen); - if (totalLen > maxCommandLineLen()) - { - int rspFd; - llvm::SmallString<128> rspPath; - if (ls::fs::createUniqueFile("ldmd-%%-%%-%%-%%.rsp", rspFd, rspPath)) - { - error("Could not open temporary response file."); - } - - { - llvm::raw_fd_ostream rspOut(rspFd, /*shouldClose=*/true); - for (auto arg : args) - rspOut << arg << '\n'; - } - - std::string rspArg = "@"; - rspArg += rspPath.str(); - - std::vector newArgs; - newArgs.push_back(argv[0]); - newArgs.push_back(rspArg.c_str()); - newArgs.push_back(NULL); - - int rc = execute(ldcPath, &newArgs[0]); - - if (ls::fs::remove(rspPath.str())) - { - warning("Could not remove response file."); - } - - return rc; - } - else - { - return execute(ldcPath, &args[0]); - } + return rc; + } else { + return execute(ldcPath, &args[0]); + } } diff --git a/driver/linker.cpp b/driver/linker.cpp index a30df41df3..364ae19700 100644 --- a/driver/linker.cpp +++ b/driver/linker.cpp @@ -29,655 +29,623 @@ ////////////////////////////////////////////////////////////////////////////// -static bool endsWith(const std::string &str, const std::string &end) -{ - return (str.length() >= end.length() && std::equal(end.rbegin(), end.rend(), str.rbegin())); +static bool endsWith(const std::string &str, const std::string &end) { + return (str.length() >= end.length() && + std::equal(end.rbegin(), end.rend(), str.rbegin())); } ////////////////////////////////////////////////////////////////////////////// -static void CreateDirectoryOnDisk(llvm::StringRef fileName) -{ - llvm::StringRef dir(llvm::sys::path::parent_path(fileName)); - if (!dir.empty() && !llvm::sys::fs::exists(dir)) - { - std::error_code ec = llvm::sys::fs::create_directory(dir); - if (ec) - { - error(Loc(), "failed to create path to file: %s\n%s", dir.data(), ec.message().c_str()); - fatal(); - } +static void CreateDirectoryOnDisk(llvm::StringRef fileName) { + llvm::StringRef dir(llvm::sys::path::parent_path(fileName)); + if (!dir.empty() && !llvm::sys::fs::exists(dir)) { + std::error_code ec = llvm::sys::fs::create_directory(dir); + if (ec) { + error(Loc(), "failed to create path to file: %s\n%s", dir.data(), + ec.message().c_str()); + fatal(); } + } } ////////////////////////////////////////////////////////////////////////////// -static std::string getOutputName(bool const sharedLib) -{ - if (!sharedLib && global.params.exefile) - { - return global.params.exefile; +static std::string getOutputName(bool const sharedLib) { + if (!sharedLib && global.params.exefile) { + return global.params.exefile; + } + + if (sharedLib && global.params.objname) { + return global.params.objname; + } + + // Output name is inferred. + std::string result; + + // try root module name + if (Module::rootModule) + result = Module::rootModule->toChars(); + else if (global.params.objfiles->dim) + result = FileName::removeExt( + static_cast(global.params.objfiles->data[0])); + else + result = "a.out"; + + if (sharedLib) { + std::string libExt = std::string(".") + global.dll_ext; + if (!endsWith(result, libExt)) { + if (global.params.targetTriple.getOS() != llvm::Triple::Win32) + result = "lib" + result + libExt; + else + result.append(libExt); } + } else if (global.params.targetTriple.isOSWindows() && + !endsWith(result, ".exe")) { + result.append(".exe"); + } - if (sharedLib && global.params.objname) - { - return global.params.objname; - } - - // Output name is inferred. - std::string result; - - // try root module name - if (Module::rootModule) - result = Module::rootModule->toChars(); - else if (global.params.objfiles->dim) - result = FileName::removeExt(static_cast(global.params.objfiles->data[0])); - else - result = "a.out"; - - if (sharedLib) - { - std::string libExt = std::string(".") + global.dll_ext; - if (!endsWith(result, libExt)) - { - if (global.params.targetTriple.getOS() != llvm::Triple::Win32) - result = "lib" + result + libExt; - else - result.append(libExt); - } - } - else if (global.params.targetTriple.isOSWindows() && !endsWith(result, ".exe")) - { - result.append(".exe"); - } - - return result; + return result; } ////////////////////////////////////////////////////////////////////////////// static std::string gExePath; -static int linkObjToBinaryGcc(bool sharedLib) -{ - Logger::println("*** Linking executable ***"); +static int linkObjToBinaryGcc(bool sharedLib) { + Logger::println("*** Linking executable ***"); - // find gcc for linking - std::string gcc(getGcc()); + // find gcc for linking + std::string gcc(getGcc()); - // build arguments - std::vector args; + // build arguments + std::vector args; - // object files - for (unsigned i = 0; i < global.params.objfiles->dim; i++) - { - const char *p = static_cast(global.params.objfiles->data[i]); - args.push_back(p); + // object files + for (unsigned i = 0; i < global.params.objfiles->dim; i++) { + const char *p = static_cast(global.params.objfiles->data[i]); + args.push_back(p); + } + + // user libs + for (unsigned i = 0; i < global.params.libfiles->dim; i++) { + const char *p = static_cast(global.params.libfiles->data[i]); + args.push_back(p); + } + + // output filename + std::string output = getOutputName(sharedLib); + + if (sharedLib) + args.push_back("-shared"); + + args.push_back("-o"); + args.push_back(output); + + // set the global gExePath + gExePath = output; + // assert(gExePath.isValid()); + + // create path to exe + CreateDirectoryOnDisk(gExePath); + + // Pass sanitizer arguments to linker. Requires clang. + if (opts::sanitize == opts::AddressSanitizer) { + args.push_back("-fsanitize=address"); + } + + if (opts::sanitize == opts::MemorySanitizer) { + args.push_back("-fsanitize=memory"); + } + + if (opts::sanitize == opts::ThreadSanitizer) { + args.push_back("-fsanitize=thread"); + } + + // additional linker switches + for (unsigned i = 0; i < global.params.linkswitches->dim; i++) { + const char *p = + static_cast(global.params.linkswitches->data[i]); + // Don't push -l and -L switches using -Xlinker, but pass them indirectly + // via GCC. This makes sure user-defined paths take precedence over + // GCC's builtin LIBRARY_PATHs. + if (!p[0] || !(p[0] == '-' && (p[1] == 'l' || p[1] == 'L'))) + args.push_back("-Xlinker"); + args.push_back(p); + } + + // default libs + bool addSoname = false; + switch (global.params.targetTriple.getOS()) { + case llvm::Triple::Linux: + addSoname = true; + args.push_back("-lrt"); + if (!opts::disableLinkerStripDead) { + args.push_back("-Wl,--gc-sections"); } + // fallthrough + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + args.push_back("-ldl"); + // fallthrough + case llvm::Triple::FreeBSD: + addSoname = true; + args.push_back("-lpthread"); + args.push_back("-lm"); + break; - // user libs - for (unsigned i = 0; i < global.params.libfiles->dim; i++) - { - const char *p = static_cast(global.params.libfiles->data[i]); - args.push_back(p); - } + case llvm::Triple::Solaris: + args.push_back("-lm"); + args.push_back("-lumem"); + // solaris TODO + break; - // output filename - std::string output = getOutputName(sharedLib); + default: + // OS not yet handled, will probably lead to linker errors. + // FIXME: Win32. + break; + } - if (sharedLib) - args.push_back("-shared"); + if (global.params.targetTriple.isWindowsGNUEnvironment()) { + // This is really more of a kludge, as linking in the Winsock functions + // should be handled by the pragma(lib, ...) in std.socket, but it + // makes LDC behave as expected for now. + args.push_back("-lws2_32"); + } - args.push_back("-o"); - args.push_back(output); - - // set the global gExePath - gExePath = output; - //assert(gExePath.isValid()); - - // create path to exe - CreateDirectoryOnDisk(gExePath); - - // Pass sanitizer arguments to linker. Requires clang. - if (opts::sanitize == opts::AddressSanitizer) { - args.push_back("-fsanitize=address"); - } - - if (opts::sanitize == opts::MemorySanitizer) { - args.push_back("-fsanitize=memory"); - } - - if (opts::sanitize == opts::ThreadSanitizer) { - args.push_back("-fsanitize=thread"); - } - - // additional linker switches - for (unsigned i = 0; i < global.params.linkswitches->dim; i++) - { - const char *p = static_cast(global.params.linkswitches->data[i]); - // Don't push -l and -L switches using -Xlinker, but pass them indirectly - // via GCC. This makes sure user-defined paths take precedence over - // GCC's builtin LIBRARY_PATHs. - if (!p[0] || !(p[0] == '-' && (p[1] == 'l' || p[1] == 'L'))) - args.push_back("-Xlinker"); - args.push_back(p); - } - - // default libs - bool addSoname = false; - switch (global.params.targetTriple.getOS()) { - case llvm::Triple::Linux: - addSoname = true; - args.push_back("-lrt"); - if (!opts::disableLinkerStripDead) { - args.push_back("-Wl,--gc-sections"); - } - // fallthrough - case llvm::Triple::Darwin: - case llvm::Triple::MacOSX: - args.push_back("-ldl"); - // fallthrough - case llvm::Triple::FreeBSD: - addSoname = true; - args.push_back("-lpthread"); - args.push_back("-lm"); - break; - - case llvm::Triple::Solaris: - args.push_back("-lm"); - args.push_back("-lumem"); - // solaris TODO - break; - - default: - // OS not yet handled, will probably lead to linker errors. - // FIXME: Win32. - break; - } - - if (global.params.targetTriple.isWindowsGNUEnvironment()) - { - // This is really more of a kludge, as linking in the Winsock functions - // should be handled by the pragma(lib, ...) in std.socket, but it - // makes LDC behave as expected for now. - args.push_back("-lws2_32"); - } - - // Only specify -m32/-m64 for architectures where the two variants actually - // exist (as e.g. the GCC ARM toolchain doesn't recognize the switches). - // MIPS does not have -m32/-m64 but requires -mabi=. - if (global.params.targetTriple.get64BitArchVariant().getArch() != - llvm::Triple::UnknownArch && - global.params.targetTriple.get32BitArchVariant().getArch() != - llvm::Triple::UnknownArch) { - if (global.params.targetTriple.get64BitArchVariant().getArch() == + // Only specify -m32/-m64 for architectures where the two variants actually + // exist (as e.g. the GCC ARM toolchain doesn't recognize the switches). + // MIPS does not have -m32/-m64 but requires -mabi=. + if (global.params.targetTriple.get64BitArchVariant().getArch() != + llvm::Triple::UnknownArch && + global.params.targetTriple.get32BitArchVariant().getArch() != + llvm::Triple::UnknownArch) { + if (global.params.targetTriple.get64BitArchVariant().getArch() == llvm::Triple::mips64 || - global.params.targetTriple.get64BitArchVariant().getArch() == + global.params.targetTriple.get64BitArchVariant().getArch() == llvm::Triple::mips64el) { - switch (getMipsABI()) - { - case MipsABI::EABI: - args.push_back("-mabi=eabi"); - break; - case MipsABI::O32: - args.push_back("-mabi=32"); - break; - case MipsABI::N32: - args.push_back("-mabi=n32"); - break; - case MipsABI::N64: - args.push_back("-mabi=64"); - break; - case MipsABI::Unknown: - break; - } - } - else { - if (global.params.is64bit) - args.push_back("-m64"); - else - args.push_back("-m32"); - } + switch (getMipsABI()) { + case MipsABI::EABI: + args.push_back("-mabi=eabi"); + break; + case MipsABI::O32: + args.push_back("-mabi=32"); + break; + case MipsABI::N32: + args.push_back("-mabi=n32"); + break; + case MipsABI::N64: + args.push_back("-mabi=64"); + break; + case MipsABI::Unknown: + break; + } + } else { + if (global.params.is64bit) + args.push_back("-m64"); + else + args.push_back("-m32"); } + } - if (opts::createSharedLib && addSoname) { - std::string soname = opts::soname; - if (!soname.empty()) { - args.push_back("-Wl,-soname," + soname); - } + if (opts::createSharedLib && addSoname) { + std::string soname = opts::soname; + if (!soname.empty()) { + args.push_back("-Wl,-soname," + soname); } + } - Logger::println("Linking with: "); - Stream logstr = Logger::cout(); - for (const auto& arg : args) - if (!arg.empty()) - logstr << "'" << arg << "'" << " "; - logstr << "\n"; // FIXME where's flush ? + Logger::println("Linking with: "); + Stream logstr = Logger::cout(); + for (const auto &arg : args) + if (!arg.empty()) + logstr << "'" << arg << "'" + << " "; + logstr << "\n"; // FIXME where's flush ? - // try to call linker - return executeToolAndWait(gcc, args, global.params.verbose); + // try to call linker + return executeToolAndWait(gcc, args, global.params.verbose); } ////////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 -namespace windows -{ - bool needsQuotes(const llvm::StringRef& arg) - { - return // not already quoted - !(arg.size() > 1 && arg[0] == '"' && arg.back() == '"') - && // empty or min 1 space or min 1 double quote - (arg.empty() || arg.find(' ') != arg.npos || arg.find('"') != arg.npos); - } - - size_t countPrecedingBackslashes(const std::string& arg, size_t index) - { - size_t count = 0; - - for (size_t i = index - 1; i >= 0; --i) - { - if (arg[i] != '\\') - break; - ++count; - } - - return count; - } - - std::string quoteArg(const std::string& arg) - { - if (!needsQuotes(arg)) - return arg; - - std::string quotedArg; - quotedArg.reserve(3 + 2 * arg.size()); // worst case - - quotedArg.push_back('"'); - - const size_t argLength = arg.length(); - for (size_t i = 0; i < argLength; ++i) - { - if (arg[i] == '"') - { - // Escape all preceding backslashes (if any). - // Note that we *don't* need to escape runs of backslashes that don't - // precede a double quote! See MSDN: - // http://msdn.microsoft.com/en-us/library/17w5ykft%28v=vs.85%29.aspx - quotedArg.append(countPrecedingBackslashes(arg, i), '\\'); - - // Escape the double quote. - quotedArg.push_back('\\'); - } - - quotedArg.push_back(arg[i]); - } - - // Make sure our final double quote doesn't get escaped by a trailing backslash. - quotedArg.append(countPrecedingBackslashes(arg, argLength), '\\'); - quotedArg.push_back('"'); - - return quotedArg; - } - - int executeAndWait(const char* commandLine) - { - STARTUPINFO si; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - PROCESS_INFORMATION pi; - ZeroMemory(&pi, sizeof(pi)); - - DWORD exitCode; - - // according to MSDN, only CreateProcessW (unicode) may modify the passed command line - if (!CreateProcess(NULL, const_cast(commandLine), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) - { - exitCode = -1; - } - else - { - if (WaitForSingleObject(pi.hProcess, INFINITE) != 0 || - !GetExitCodeProcess(pi.hProcess, &exitCode)) - exitCode = -2; - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } - - return exitCode; - } +namespace windows { +bool needsQuotes(const llvm::StringRef &arg) { + return // not already quoted + !(arg.size() > 1 && arg[0] == '"' && + arg.back() == '"') && // empty or min 1 space or min 1 double quote + (arg.empty() || arg.find(' ') != arg.npos || arg.find('"') != arg.npos); } -int executeMsvcToolAndWait(const std::string& tool, const std::vector& args, bool verbose) -{ - llvm::SmallString<1024> commandLine; // full command line incl. executable +size_t countPrecedingBackslashes(const std::string &arg, size_t index) { + size_t count = 0; - // if the VSINSTALLDIR environment variable is NOT set, - // the MSVC environment needs to be set up - const bool needMsvcSetup = !getenv("VSINSTALLDIR"); + for (size_t i = index - 1; i >= 0; --i) { + if (arg[i] != '\\') + break; + ++count; + } + + return count; +} + +std::string quoteArg(const std::string &arg) { + if (!needsQuotes(arg)) + return arg; + + std::string quotedArg; + quotedArg.reserve(3 + 2 * arg.size()); // worst case + + quotedArg.push_back('"'); + + const size_t argLength = arg.length(); + for (size_t i = 0; i < argLength; ++i) { + if (arg[i] == '"') { + // Escape all preceding backslashes (if any). + // Note that we *don't* need to escape runs of backslashes that don't + // precede a double quote! See MSDN: + // http://msdn.microsoft.com/en-us/library/17w5ykft%28v=vs.85%29.aspx + quotedArg.append(countPrecedingBackslashes(arg, i), '\\'); + + // Escape the double quote. + quotedArg.push_back('\\'); + } + + quotedArg.push_back(arg[i]); + } + + // Make sure our final double quote doesn't get escaped by a trailing + // backslash. + quotedArg.append(countPrecedingBackslashes(arg, argLength), '\\'); + quotedArg.push_back('"'); + + return quotedArg; +} + +int executeAndWait(const char *commandLine) { + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + + DWORD exitCode; + + // according to MSDN, only CreateProcessW (unicode) may modify the passed + // command line + if (!CreateProcess(NULL, const_cast(commandLine), NULL, NULL, TRUE, 0, + NULL, NULL, &si, &pi)) { + exitCode = -1; + } else { + if (WaitForSingleObject(pi.hProcess, INFINITE) != 0 || + !GetExitCodeProcess(pi.hProcess, &exitCode)) + exitCode = -2; + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + + return exitCode; +} +} + +int executeMsvcToolAndWait(const std::string &tool, + const std::vector &args, bool verbose) { + llvm::SmallString<1024> commandLine; // full command line incl. executable + + // if the VSINSTALLDIR environment variable is NOT set, + // the MSVC environment needs to be set up + const bool needMsvcSetup = !getenv("VSINSTALLDIR"); + if (needMsvcSetup) { + /* => %ComSpec% /s /c " " + * + * cmd.exe /c treats the following string argument (the command) + * in a very peculiar way if it starts with a double-quote. + * By adding /s and enclosing the command in extra double-quotes + * (WITHOUT additionally escaping the command), the command will + * be parsed properly. + */ + + std::string cmdPath = getenv("ComSpec"); + std::string batchFile = exe_path::prependBinDir( + global.params.targetTriple.isArch64Bit() ? "amd64.bat" : "x86.bat"); + + commandLine.append(windows::quoteArg(cmdPath)); + commandLine.append(" /s /c \""); + commandLine.append(windows::quoteArg(batchFile)); + commandLine.push_back(' '); + commandLine.append(windows::quoteArg(tool)); + } else { + std::string toolPath = getProgram(tool.c_str()); + commandLine.append(windows::quoteArg(toolPath)); + } + + const size_t commandLineLengthAfterTool = commandLine.size(); + + // append (quoted) args + for (size_t i = 0; i < args.size(); ++i) { + commandLine.push_back(' '); + commandLine.append(windows::quoteArg(args[i])); + } + + const bool useResponseFile = (!args.empty() && commandLine.size() > 2000); + llvm::SmallString<128> responseFilePath; + if (useResponseFile) { + const size_t firstArgIndex = commandLineLengthAfterTool + 1; + llvm::StringRef content(commandLine.data() + firstArgIndex, + commandLine.size() - firstArgIndex); + + if (llvm::sys::fs::createTemporaryFile("ldc_link", "rsp", + responseFilePath) || + llvm::sys::writeFileWithEncoding( + responseFilePath, + content)) // keep encoding (LLVM assumes UTF-8 input) + { + error(Loc(), "cannot write temporary response file for %s", tool.c_str()); + return -1; + } + + // replace all args by @ + std::string responseFileArg = ("@" + responseFilePath).str(); + commandLine.resize(firstArgIndex); + commandLine.append(windows::quoteArg(responseFileArg)); + } + + if (needMsvcSetup) + commandLine.push_back('"'); + + const char *finalCommandLine = commandLine.c_str(); + + if (verbose) { + fprintf(global.stdmsg, finalCommandLine); + fprintf(global.stdmsg, "\n"); + fflush(global.stdmsg); + } + + const int exitCode = windows::executeAndWait(finalCommandLine); + + if (exitCode != 0) { + commandLine.resize(commandLineLengthAfterTool); if (needMsvcSetup) - { - /* => %ComSpec% /s /c " " - * - * cmd.exe /c treats the following string argument (the command) - * in a very peculiar way if it starts with a double-quote. - * By adding /s and enclosing the command in extra double-quotes - * (WITHOUT additionally escaping the command), the command will - * be parsed properly. - */ + commandLine.push_back('"'); + error(Loc(), "`%s` failed with status: %d", commandLine.c_str(), exitCode); + } - std::string cmdPath = getenv("ComSpec"); - std::string batchFile = exe_path::prependBinDir( - global.params.targetTriple.isArch64Bit() ? "amd64.bat" : "x86.bat"); + if (useResponseFile) + llvm::sys::fs::remove(responseFilePath); - commandLine.append(windows::quoteArg(cmdPath)); - commandLine.append(" /s /c \""); - commandLine.append(windows::quoteArg(batchFile)); - commandLine.push_back(' '); - commandLine.append(windows::quoteArg(tool)); - } - else - { - std::string toolPath = getProgram(tool.c_str()); - commandLine.append(windows::quoteArg(toolPath)); - } - - const size_t commandLineLengthAfterTool = commandLine.size(); - - // append (quoted) args - for (size_t i = 0; i < args.size(); ++i) - { - commandLine.push_back(' '); - commandLine.append(windows::quoteArg(args[i])); - } - - const bool useResponseFile = (!args.empty() && commandLine.size() > 2000); - llvm::SmallString<128> responseFilePath; - if (useResponseFile) - { - const size_t firstArgIndex = commandLineLengthAfterTool + 1; - llvm::StringRef content(commandLine.data() + firstArgIndex, commandLine.size() - firstArgIndex); - - if (llvm::sys::fs::createTemporaryFile("ldc_link", "rsp", responseFilePath) || - llvm::sys::writeFileWithEncoding(responseFilePath, content)) // keep encoding (LLVM assumes UTF-8 input) - { - error(Loc(), "cannot write temporary response file for %s", tool.c_str()); - return -1; - } - - // replace all args by @ - std::string responseFileArg = ("@" + responseFilePath).str(); - commandLine.resize(firstArgIndex); - commandLine.append(windows::quoteArg(responseFileArg)); - } - - if (needMsvcSetup) - commandLine.push_back('"'); - - const char* finalCommandLine = commandLine.c_str(); - - if (verbose) - { - fprintf(global.stdmsg, finalCommandLine); - fprintf(global.stdmsg, "\n"); - fflush(global.stdmsg); - } - - const int exitCode = windows::executeAndWait(finalCommandLine); - - if (exitCode != 0) - { - commandLine.resize(commandLineLengthAfterTool); - if (needMsvcSetup) - commandLine.push_back('"'); - error(Loc(), "`%s` failed with status: %d", commandLine.c_str(), exitCode); - } - - if (useResponseFile) - llvm::sys::fs::remove(responseFilePath); - - return exitCode; + return exitCode; } #else // !_WIN32 -int executeMsvcToolAndWait(const std::string&, const std::vector&, bool) -{ - assert(0); - return -1; +int executeMsvcToolAndWait(const std::string &, + const std::vector &, bool) { + assert(0); + return -1; } #endif ////////////////////////////////////////////////////////////////////////////// -static int linkObjToBinaryWin(bool sharedLib) -{ - Logger::println("*** Linking executable ***"); +static int linkObjToBinaryWin(bool sharedLib) { + Logger::println("*** Linking executable ***"); - std::string tool = "link.exe"; + std::string tool = "link.exe"; - // build arguments - std::vector args; + // build arguments + std::vector args; + args.push_back("/NOLOGO"); + + // specify that the image will contain a table of safe exception handlers + // (32bit only) + if (!global.params.is64bit) + args.push_back("/SAFESEH"); + + // because of a LLVM bug, see LDC issue 442 + if (global.params.symdebug) + args.push_back("/LARGEADDRESSAWARE:NO"); + else + args.push_back("/LARGEADDRESSAWARE"); + + // output debug information + if (global.params.symdebug) + args.push_back("/DEBUG"); + + // enable Link-time Code Generation (aka. whole program optimization) + if (global.params.optimize) + args.push_back("/LTCG"); + + // remove dead code and fold identical COMDATs + if (opts::disableLinkerStripDead) + args.push_back("/OPT:NOREF"); + else { + args.push_back("/OPT:REF"); + args.push_back("/OPT:ICF"); + } + + // specify creation of DLL + if (sharedLib) + args.push_back("/DLL"); + + // output filename + std::string output = getOutputName(sharedLib); + + args.push_back("/OUT:" + output); + + // object files + for (unsigned i = 0; i < global.params.objfiles->dim; i++) { + const char *p = static_cast(global.params.objfiles->data[i]); + args.push_back(p); + } + + // user libs + for (unsigned i = 0; i < global.params.libfiles->dim; i++) { + const char *p = static_cast(global.params.libfiles->data[i]); + args.push_back(p); + } + + // set the global gExePath + gExePath = output; + // assert(gExePath.isValid()); + + // create path to exe + CreateDirectoryOnDisk(gExePath); + + // additional linker switches + for (unsigned i = 0; i < global.params.linkswitches->dim; i++) { + std::string str = global.params.linkswitches->data[i]; + if (str.length() > 2) { + // rewrite common -L and -l switches + if (str[0] == '-' && str[1] == 'L') + str = "/LIBPATH:" + str.substr(2); + else if (str[0] == '-' && str[1] == 'l') { + str = str.substr(2) + ".lib"; + } + } + args.push_back(str); + } + + // default libs + // TODO check which libaries are necessary + args.push_back("kernel32.lib"); + args.push_back("user32.lib"); + args.push_back("gdi32.lib"); + args.push_back("winspool.lib"); + args.push_back("shell32.lib"); // required for dmain2.d + args.push_back("ole32.lib"); + args.push_back("oleaut32.lib"); + args.push_back("uuid.lib"); + args.push_back("comdlg32.lib"); + args.push_back("advapi32.lib"); + + Logger::println("Linking with: "); + Stream logstr = Logger::cout(); + for (const auto &arg : args) + if (!arg.empty()) + logstr << "'" << arg << "'" + << " "; + logstr << "\n"; // FIXME where's flush ? + + // try to call linker + return executeMsvcToolAndWait(tool, args, global.params.verbose); +} + +////////////////////////////////////////////////////////////////////////////// + +int linkObjToBinary(bool sharedLib) { + int exitCode; + if (global.params.targetTriple.isWindowsMSVCEnvironment()) + exitCode = linkObjToBinaryWin(sharedLib); + else + exitCode = linkObjToBinaryGcc(sharedLib); + return exitCode; +} + +////////////////////////////////////////////////////////////////////////////// + +int createStaticLibrary() { + Logger::println("*** Creating static library ***"); + + const bool isTargetWindows = + global.params.targetTriple.isWindowsMSVCEnvironment(); + + // find archiver + std::string tool(isTargetWindows ? "lib.exe" : getArchiver()); + + // build arguments + std::vector args; + + // ask ar to create a new library + if (!isTargetWindows) + args.push_back("rcs"); + + // ask lib to be quiet + if (isTargetWindows) args.push_back("/NOLOGO"); - // specify that the image will contain a table of safe exception handlers (32bit only) - if (!global.params.is64bit) - args.push_back("/SAFESEH"); + // enable Link-time Code Generation (aka. whole program optimization) + if (isTargetWindows && global.params.optimize) + args.push_back("/LTCG"); - // because of a LLVM bug, see LDC issue 442 - if (global.params.symdebug) - args.push_back("/LARGEADDRESSAWARE:NO"); + // output filename + std::string libName; + if (global.params.objname) { // explicit + libName = global.params.objname; + } else { // inferred + // try root module name + if (Module::rootModule) + libName = Module::rootModule->toChars(); + else if (global.params.objfiles->dim) + libName = FileName::removeExt( + static_cast(global.params.objfiles->data[0])); else - args.push_back("/LARGEADDRESSAWARE"); + libName = "a.out"; + } + // KN: The following lines were added to fix a test case failure + // (runnable/test13774.sh). + // Root cause is that dmd handles it in this why. + // As a side effect this change broke compiling with dub. + // if (!FileName::absolute(libName.c_str())) + // libName = FileName::combine(global.params.objdir, libName.c_str()); + if (llvm::sys::path::extension(libName).empty()) + libName.append(std::string(".") + global.lib_ext); + if (isTargetWindows) + args.push_back("/OUT:" + libName); + else + args.push_back(libName); - // output debug information - if (global.params.symdebug) - args.push_back("/DEBUG"); + // object files + for (unsigned i = 0; i < global.params.objfiles->dim; i++) { + const char *p = static_cast(global.params.objfiles->data[i]); + args.push_back(p); + } - // enable Link-time Code Generation (aka. whole program optimization) - if (global.params.optimize) - args.push_back("/LTCG"); + // create path to the library + CreateDirectoryOnDisk(libName); - // remove dead code and fold identical COMDATs - if (opts::disableLinkerStripDead) - args.push_back("/OPT:NOREF"); - else - { - args.push_back("/OPT:REF"); - args.push_back("/OPT:ICF"); - } - - // specify creation of DLL - if (sharedLib) - args.push_back("/DLL"); - - // output filename - std::string output = getOutputName(sharedLib); - - args.push_back("/OUT:" + output); - - // object files - for (unsigned i = 0; i < global.params.objfiles->dim; i++) - { - const char *p = static_cast(global.params.objfiles->data[i]); - args.push_back(p); - } - - // user libs - for (unsigned i = 0; i < global.params.libfiles->dim; i++) - { - const char *p = static_cast(global.params.libfiles->data[i]); - args.push_back(p); - } - - // set the global gExePath - gExePath = output; - //assert(gExePath.isValid()); - - // create path to exe - CreateDirectoryOnDisk(gExePath); - - // additional linker switches - for (unsigned i = 0; i < global.params.linkswitches->dim; i++) - { - std::string str = global.params.linkswitches->data[i]; - if (str.length() > 2) - { - // rewrite common -L and -l switches - if (str[0] == '-' && str[1] == 'L') - str = "/LIBPATH:" + str.substr(2); - else if (str[0] == '-' && str[1] == 'l') - { - str = str.substr(2) + ".lib"; - } - } - args.push_back(str); - } - - // default libs - // TODO check which libaries are necessary - args.push_back("kernel32.lib"); - args.push_back("user32.lib"); - args.push_back("gdi32.lib"); - args.push_back("winspool.lib"); - args.push_back("shell32.lib"); // required for dmain2.d - args.push_back("ole32.lib"); - args.push_back("oleaut32.lib"); - args.push_back("uuid.lib"); - args.push_back("comdlg32.lib"); - args.push_back("advapi32.lib"); - - Logger::println("Linking with: "); - Stream logstr = Logger::cout(); - for (const auto& arg : args) - if (!arg.empty()) - logstr << "'" << arg << "'" << " "; - logstr << "\n"; // FIXME where's flush ? - - // try to call linker - return executeMsvcToolAndWait(tool, args, global.params.verbose); + // try to call archiver + int exitCode; + if (isTargetWindows) + exitCode = executeMsvcToolAndWait(tool, args, global.params.verbose); + else + exitCode = executeToolAndWait(tool, args, global.params.verbose); + return exitCode; } ////////////////////////////////////////////////////////////////////////////// -int linkObjToBinary(bool sharedLib) -{ - int exitCode; - if (global.params.targetTriple.isWindowsMSVCEnvironment()) - exitCode = linkObjToBinaryWin(sharedLib); - else - exitCode = linkObjToBinaryGcc(sharedLib); - return exitCode; +void deleteExecutable() { + if (!gExePath.empty()) { + // assert(gExePath.isValid()); + bool is_directory; + assert(!(!llvm::sys::fs::is_directory(gExePath, is_directory) && + is_directory)); + llvm::sys::fs::remove(gExePath); + } } ////////////////////////////////////////////////////////////////////////////// -int createStaticLibrary() -{ - Logger::println("*** Creating static library ***"); +int runExecutable() { + assert(!gExePath.empty()); + // assert(gExePath.isValid()); - const bool isTargetWindows = global.params.targetTriple.isWindowsMSVCEnvironment(); - - // find archiver - std::string tool(isTargetWindows ? "lib.exe" : getArchiver()); - - // build arguments - std::vector args; - - // ask ar to create a new library - if (!isTargetWindows) - args.push_back("rcs"); - - // ask lib to be quiet - if (isTargetWindows) - args.push_back("/NOLOGO"); - - // enable Link-time Code Generation (aka. whole program optimization) - if (isTargetWindows && global.params.optimize) - args.push_back("/LTCG"); - - // output filename - std::string libName; - if (global.params.objname) - { // explicit - libName = global.params.objname; - } - else - { // inferred - // try root module name - if (Module::rootModule) - libName = Module::rootModule->toChars(); - else if (global.params.objfiles->dim) - libName = FileName::removeExt(static_cast(global.params.objfiles->data[0])); - else - libName = "a.out"; - } -// KN: The following lines were added to fix a test case failure (runnable/test13774.sh). -// Root cause is that dmd handles it in this why. -// As a side effect this change broke compiling with dub. -// if (!FileName::absolute(libName.c_str())) -// libName = FileName::combine(global.params.objdir, libName.c_str()); - if (llvm::sys::path::extension(libName).empty()) - libName.append(std::string(".") + global.lib_ext); - if (isTargetWindows) - args.push_back("/OUT:" + libName); - else - args.push_back(libName); - - // object files - for (unsigned i = 0; i < global.params.objfiles->dim; i++) - { - const char *p = static_cast(global.params.objfiles->data[i]); - args.push_back(p); - } - - // create path to the library - CreateDirectoryOnDisk(libName); - - // try to call archiver - int exitCode; - if (isTargetWindows) - exitCode = executeMsvcToolAndWait(tool, args, global.params.verbose); - else - exitCode = executeToolAndWait(tool, args, global.params.verbose); - return exitCode; -} - -////////////////////////////////////////////////////////////////////////////// - -void deleteExecutable() -{ - if (!gExePath.empty()) - { - //assert(gExePath.isValid()); - bool is_directory; - assert(!(!llvm::sys::fs::is_directory(gExePath, is_directory) && is_directory)); - llvm::sys::fs::remove(gExePath); - } -} - -////////////////////////////////////////////////////////////////////////////// - -int runExecutable() -{ - assert(!gExePath.empty()); - //assert(gExePath.isValid()); - - // Run executable - int status = executeToolAndWait(gExePath, opts::runargs, global.params.verbose); - if (status < 0) - { + // Run executable + int status = + executeToolAndWait(gExePath, opts::runargs, global.params.verbose); + if (status < 0) { #if defined(_MSC_VER) || defined(__MINGW32__) - error(Loc(), "program received signal %d", -status); + error(Loc(), "program received signal %d", -status); #else - error(Loc(), "program received signal %d (%s)", -status, strsignal(-status)); + error(Loc(), "program received signal %d (%s)", -status, + strsignal(-status)); #endif - return -status; - } - return status; + return -status; + } + return status; } diff --git a/driver/main.cpp b/driver/main.cpp index 44ede198b4..d39b791b76 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -69,1267 +69,1203 @@ void initTraitsStringTable(); using namespace opts; -extern void getenv_setargv(const char *envvar, int *pargc, char** *pargv); +extern void getenv_setargv(const char *envvar, int *pargc, char ***pargv); -static cl::opt noDefaultLib("nodefaultlib", - cl::desc("Don't add a default library for linking implicitly"), - cl::ZeroOrMore, - cl::Hidden); +static cl::opt + noDefaultLib("nodefaultlib", + cl::desc("Don't add a default library for linking implicitly"), + cl::ZeroOrMore, cl::Hidden); static StringsAdapter impPathsStore("I", global.params.imppath); -static cl::list importPaths("I", - cl::desc("Where to look for imports"), - cl::value_desc("path"), - cl::location(impPathsStore), - cl::Prefix); +static cl::list + importPaths("I", cl::desc("Where to look for imports"), + cl::value_desc("path"), cl::location(impPathsStore), + cl::Prefix); -static cl::opt defaultLib("defaultlib", - cl::desc("Default libraries to link with (overrides previous)"), - cl::value_desc("lib1,lib2,..."), - cl::ZeroOrMore); +static cl::opt + defaultLib("defaultlib", + cl::desc("Default libraries to link with (overrides previous)"), + cl::value_desc("lib1,lib2,..."), cl::ZeroOrMore); -static cl::opt debugLib("debuglib", +static cl::opt debugLib( + "debuglib", cl::desc("Debug versions of default libraries (overrides previous)"), - cl::value_desc("lib1,lib2,..."), - cl::ZeroOrMore); + cl::value_desc("lib1,lib2,..."), cl::ZeroOrMore); -static cl::opt linkDebugLib("link-debuglib", +static cl::opt linkDebugLib( + "link-debuglib", cl::desc("Link with libraries specified in -debuglib, not -defaultlib"), cl::ZeroOrMore); void printVersion() { - printf("LDC - the LLVM D compiler (%s):\n", global.ldc_version); - printf(" based on DMD %s and LLVM %s\n", global.version, global.llvm_version); + printf("LDC - the LLVM D compiler (%s):\n", global.ldc_version); + printf(" based on DMD %s and LLVM %s\n", global.version, + global.llvm_version); #if defined(__has_feature) #if __has_feature(address_sanitizer) - printf(" compiled with address sanitizer enabled\n"); + printf(" compiled with address sanitizer enabled\n"); #endif #endif - printf(" Default target: %s\n", llvm::sys::getDefaultTargetTriple().c_str()); - std::string CPU = llvm::sys::getHostCPUName(); - if (CPU == "generic") CPU = "(unknown)"; - printf(" Host CPU: %s\n", CPU.c_str()); - printf(" http://dlang.org - http://wiki.dlang.org/LDC\n"); - printf("\n"); + printf(" Default target: %s\n", llvm::sys::getDefaultTargetTriple().c_str()); + std::string CPU = llvm::sys::getHostCPUName(); + if (CPU == "generic") + CPU = "(unknown)"; + printf(" Host CPU: %s\n", CPU.c_str()); + printf(" http://dlang.org - http://wiki.dlang.org/LDC\n"); + printf("\n"); - // Without explicitly flushing here, only the target list is visible when - // redirecting stdout to a file. - fflush(stdout); + // Without explicitly flushing here, only the target list is visible when + // redirecting stdout to a file. + fflush(stdout); - llvm::TargetRegistry::printRegisteredTargetsForVersion(); - exit(EXIT_SUCCESS); + llvm::TargetRegistry::printRegisteredTargetsForVersion(); + exit(EXIT_SUCCESS); } // Helper function to handle -d-debug=* and -d-version=* -static void processVersions(std::vector& list, const char* type, - void (*setLevel)(unsigned), void (*addIdent)(const char*)) { - for (const auto& i : list) { - const char* value = i.c_str(); - if (isdigit(value[0])) { - errno = 0; - char* end; - long level = strtol(value, &end, 10); - if (*end || errno || level > INT_MAX) { - error(Loc(), "Invalid %s level: %s", type, i.c_str()); - } else { - setLevel((unsigned)level); - } - } else { - char* cstr = mem.xstrdup(value); - if (Lexer::isValidIdentifier(cstr)) { - addIdent(cstr); - continue; - } else { - error(Loc(), "Invalid %s identifier or level: '%s'", type, i.c_str()); - } - } +static void processVersions(std::vector &list, const char *type, + void (*setLevel)(unsigned), + void (*addIdent)(const char *)) { + for (const auto &i : list) { + const char *value = i.c_str(); + if (isdigit(value[0])) { + errno = 0; + char *end; + long level = strtol(value, &end, 10); + if (*end || errno || level > INT_MAX) { + error(Loc(), "Invalid %s level: %s", type, i.c_str()); + } else { + setLevel((unsigned)level); + } + } else { + char *cstr = mem.xstrdup(value); + if (Lexer::isValidIdentifier(cstr)) { + addIdent(cstr); + continue; + } else { + error(Loc(), "Invalid %s identifier or level: '%s'", type, i.c_str()); + } } + } } // Helper function to handle -of, -od, etc. -static void initFromString(const char*& dest, const cl::opt& src) { - dest = 0; - if (src.getNumOccurrences() != 0) { - if (src.empty()) - error(Loc(), "Expected argument to '-%s'", src.ArgStr); - dest = mem.xstrdup(src.c_str()); - } +static void initFromString(const char *&dest, const cl::opt &src) { + dest = 0; + if (src.getNumOccurrences() != 0) { + if (src.empty()) + error(Loc(), "Expected argument to '-%s'", src.ArgStr); + dest = mem.xstrdup(src.c_str()); + } } - -static void hide(llvm::StringMap& map, const char* name) { - // Check if option exists first for resilience against LLVM changes - // between versions. - if (map.count(name)) - map[name]->setHiddenFlag(cl::Hidden); +static void hide(llvm::StringMap &map, const char *name) { + // Check if option exists first for resilience against LLVM changes + // between versions. + if (map.count(name)) + map[name]->setHiddenFlag(cl::Hidden); } -static void rename(llvm::StringMap& map, const char* from, const char *to) { - auto i = map.find(from); - if (i != map.end()) - { - cl::Option *opt = i->getValue(); - map.erase(i); - opt->setArgStr(to); - map[to] = opt; - } +static void rename(llvm::StringMap &map, const char *from, + const char *to) { + auto i = map.find(from); + if (i != map.end()) { + cl::Option *opt = i->getValue(); + map.erase(i); + opt->setArgStr(to); + map[to] = opt; + } } /// Removes command line options exposed from within LLVM that are unlikely /// to be useful for end users from the -help output. static void hideLLVMOptions() { #if LDC_LLVM_VER >= 307 - llvm::StringMap& map = cl::getRegisteredOptions(); + llvm::StringMap &map = cl::getRegisteredOptions(); #else - llvm::StringMap map; - cl::getRegisteredOptions(map); + llvm::StringMap map; + cl::getRegisteredOptions(map); #endif - hide(map, "bounds-checking-single-trap"); - hide(map, "disable-debug-info-verifier"); - hide(map, "disable-objc-arc-checkforcfghazards"); - hide(map, "disable-spill-fusing"); - hide(map, "cppfname"); - hide(map, "cppfor"); - hide(map, "cppgen"); - hide(map, "enable-correct-eh-support"); - hide(map, "enable-load-pre"); - hide(map, "enable-misched"); - hide(map, "enable-objc-arc-annotations"); - hide(map, "enable-objc-arc-opts"); - hide(map, "enable-scoped-noalias"); - hide(map, "enable-tbaa"); - hide(map, "exhaustive-register-search"); - hide(map, "fatal-assembler-warnings"); - hide(map, "internalize-public-api-file"); - hide(map, "internalize-public-api-list"); - hide(map, "join-liveintervals"); - hide(map, "limit-float-precision"); - hide(map, "mc-x86-disable-arith-relaxation"); - hide(map, "mips16-constant-islands"); - hide(map, "mips16-hard-float"); - hide(map, "mlsm"); - hide(map, "mno-ldc1-sdc1"); - hide(map, "nvptx-sched4reg"); - hide(map, "no-discriminators"); - hide(map, "objc-arc-annotation-target-identifier"), - hide(map, "pre-RA-sched"); - hide(map, "print-after-all"); - hide(map, "print-before-all"); - hide(map, "print-machineinstrs"); - hide(map, "profile-estimator-loop-weight"); - hide(map, "profile-estimator-loop-weight"); - hide(map, "profile-file"); - hide(map, "profile-info-file"); - hide(map, "profile-verifier-noassert"); - hide(map, "regalloc"); - hide(map, "rewrite-map-file"); - hide(map, "rng-seed"); - hide(map, "sample-profile-max-propagate-iterations"); - hide(map, "shrink-wrap"); - hide(map, "spiller"); - hide(map, "stackmap-version"); - hide(map, "stats"); - hide(map, "strip-debug"); - hide(map, "struct-path-tbaa"); - hide(map, "time-passes"); - hide(map, "unit-at-a-time"); - hide(map, "verify-debug-info"); - hide(map, "verify-dom-info"); - hide(map, "verify-loop-info"); - hide(map, "verify-regalloc"); - hide(map, "verify-region-info"); - hide(map, "verify-scev"); - hide(map, "x86-early-ifcvt"); - hide(map, "x86-use-vzeroupper"); - hide(map, "x86-recip-refinement-steps"); + hide(map, "bounds-checking-single-trap"); + hide(map, "disable-debug-info-verifier"); + hide(map, "disable-objc-arc-checkforcfghazards"); + hide(map, "disable-spill-fusing"); + hide(map, "cppfname"); + hide(map, "cppfor"); + hide(map, "cppgen"); + hide(map, "enable-correct-eh-support"); + hide(map, "enable-load-pre"); + hide(map, "enable-misched"); + hide(map, "enable-objc-arc-annotations"); + hide(map, "enable-objc-arc-opts"); + hide(map, "enable-scoped-noalias"); + hide(map, "enable-tbaa"); + hide(map, "exhaustive-register-search"); + hide(map, "fatal-assembler-warnings"); + hide(map, "internalize-public-api-file"); + hide(map, "internalize-public-api-list"); + hide(map, "join-liveintervals"); + hide(map, "limit-float-precision"); + hide(map, "mc-x86-disable-arith-relaxation"); + hide(map, "mips16-constant-islands"); + hide(map, "mips16-hard-float"); + hide(map, "mlsm"); + hide(map, "mno-ldc1-sdc1"); + hide(map, "nvptx-sched4reg"); + hide(map, "no-discriminators"); + hide(map, "objc-arc-annotation-target-identifier"), hide(map, "pre-RA-sched"); + hide(map, "print-after-all"); + hide(map, "print-before-all"); + hide(map, "print-machineinstrs"); + hide(map, "profile-estimator-loop-weight"); + hide(map, "profile-estimator-loop-weight"); + hide(map, "profile-file"); + hide(map, "profile-info-file"); + hide(map, "profile-verifier-noassert"); + hide(map, "regalloc"); + hide(map, "rewrite-map-file"); + hide(map, "rng-seed"); + hide(map, "sample-profile-max-propagate-iterations"); + hide(map, "shrink-wrap"); + hide(map, "spiller"); + hide(map, "stackmap-version"); + hide(map, "stats"); + hide(map, "strip-debug"); + hide(map, "struct-path-tbaa"); + hide(map, "time-passes"); + hide(map, "unit-at-a-time"); + hide(map, "verify-debug-info"); + hide(map, "verify-dom-info"); + hide(map, "verify-loop-info"); + hide(map, "verify-regalloc"); + hide(map, "verify-region-info"); + hide(map, "verify-scev"); + hide(map, "x86-early-ifcvt"); + hide(map, "x86-use-vzeroupper"); + hide(map, "x86-recip-refinement-steps"); - // We enable -fdata-sections/-ffunction-sections by default where it makes - // sense for reducing code size, so hide them to avoid confusion. - // - // We need our own switch as these two are defined by LLVM and linked to - // static TargetMachine members, but the default we want to use depends - // on the target triple (and thus we do not know it until after the command - // line has been parsed). - hide(map, "fdata-sections"); - hide(map, "ffunction-sections"); + // We enable -fdata-sections/-ffunction-sections by default where it makes + // sense for reducing code size, so hide them to avoid confusion. + // + // We need our own switch as these two are defined by LLVM and linked to + // static TargetMachine members, but the default we want to use depends + // on the target triple (and thus we do not know it until after the command + // line has been parsed). + hide(map, "fdata-sections"); + hide(map, "ffunction-sections"); #if LDC_LLVM_VER >= 307 - // LLVM 3.7 introduces compiling as shared library. The result - // is a clash in the command line options. - rename(map, "color", "llvm-color"); - hide(map, "llvm-color"); - opts::CreateColorOption(); + // LLVM 3.7 introduces compiling as shared library. The result + // is a clash in the command line options. + rename(map, "color", "llvm-color"); + hide(map, "llvm-color"); + opts::CreateColorOption(); #endif } int main(int argc, char **argv); -static const char* tryGetExplicitConfFile(int argc, char** argv) -{ - // begin at the back => use latest -conf= specification - for (int i = argc - 1; i >= 1; --i) - { - if (strncmp(argv[i], "-conf=", 6) == 0) - return argv[i] + 6; - } - return 0; +static const char *tryGetExplicitConfFile(int argc, char **argv) { + // begin at the back => use latest -conf= specification + for (int i = argc - 1; i >= 1; --i) { + if (strncmp(argv[i], "-conf=", 6) == 0) + return argv[i] + 6; + } + return 0; } /// Parses switches from the command line, any response files and the global /// config file and sets up global.params accordingly. /// /// Returns a list of source file names. -static void parseCommandLine(int argc, char **argv, Strings &sourceFiles, bool &helpOnly) { - global.params.argv0 = exe_path::getExePath().data(); +static void parseCommandLine(int argc, char **argv, Strings &sourceFiles, + bool &helpOnly) { + global.params.argv0 = exe_path::getExePath().data(); - // Set some default values. - global.params.useSwitchError = 1; - global.params.color = isConsoleColorSupported(); + // Set some default values. + global.params.useSwitchError = 1; + global.params.color = isConsoleColorSupported(); - global.params.linkswitches = new Strings(); - global.params.libfiles = new Strings(); - global.params.objfiles = new Strings(); - global.params.ddocfiles = new Strings(); + global.params.linkswitches = new Strings(); + global.params.libfiles = new Strings(); + global.params.objfiles = new Strings(); + global.params.ddocfiles = new Strings(); - global.params.moduleDeps = NULL; - global.params.moduleDepsFile = NULL; + global.params.moduleDeps = NULL; + global.params.moduleDepsFile = NULL; - // Build combined list of command line arguments. - std::vector final_args; - final_args.push_back(argv[0]); + // Build combined list of command line arguments. + std::vector final_args; + final_args.push_back(argv[0]); - ConfigFile cfg_file; - const char* explicitConfFile = tryGetExplicitConfFile(argc, argv); - // just ignore errors for now, they are still printed - cfg_file.read(explicitConfFile); - final_args.insert(final_args.end(), cfg_file.switches_begin(), cfg_file.switches_end()); + ConfigFile cfg_file; + const char *explicitConfFile = tryGetExplicitConfFile(argc, argv); + // just ignore errors for now, they are still printed + cfg_file.read(explicitConfFile); + final_args.insert(final_args.end(), cfg_file.switches_begin(), + cfg_file.switches_end()); - final_args.insert(final_args.end(), &argv[1], &argv[argc]); + final_args.insert(final_args.end(), &argv[1], &argv[argc]); - cl::SetVersionPrinter(&printVersion); - hideLLVMOptions(); - cl::ParseCommandLineOptions(final_args.size(), const_cast(final_args.data()), - "LDC - the LLVM D compiler\n"); + cl::SetVersionPrinter(&printVersion); + hideLLVMOptions(); + cl::ParseCommandLineOptions(final_args.size(), + const_cast(final_args.data()), + "LDC - the LLVM D compiler\n"); - helpOnly = mCPU == "help" || - (std::find(mAttrs.begin(), mAttrs.end(), "help") != mAttrs.end()); + helpOnly = mCPU == "help" || + (std::find(mAttrs.begin(), mAttrs.end(), "help") != mAttrs.end()); - // Print some information if -v was passed - // - path to compiler binary - // - version number - // - used config file - if (global.params.verbose) { - fprintf(global.stdmsg, "binary %s\n", exe_path::getExePath().c_str()); - fprintf(global.stdmsg, "version %s (DMD %s, LLVM %s)\n", global.ldc_version, global.version, global.llvm_version); - const std::string& path = cfg_file.path(); - if (!path.empty()) - fprintf(global.stdmsg, "config %s\n", path.c_str()); + // Print some information if -v was passed + // - path to compiler binary + // - version number + // - used config file + if (global.params.verbose) { + fprintf(global.stdmsg, "binary %s\n", exe_path::getExePath().c_str()); + fprintf(global.stdmsg, "version %s (DMD %s, LLVM %s)\n", + global.ldc_version, global.version, global.llvm_version); + const std::string &path = cfg_file.path(); + if (!path.empty()) + fprintf(global.stdmsg, "config %s\n", path.c_str()); + } + + // Negated options + global.params.link = !compileOnly; + global.params.obj = !dontWriteObj; + global.params.useInlineAsm = !noAsm; + + // String options: std::string --> char* + initFromString(global.params.objname, objectFile); + initFromString(global.params.objdir, objectDir); + + initFromString(global.params.docdir, ddocDir); + initFromString(global.params.docname, ddocFile); + global.params.doDocComments |= global.params.docdir || global.params.docname; + + initFromString(global.params.jsonfilename, jsonFile); + if (global.params.jsonfilename) + global.params.doJsonGeneration = true; + + initFromString(global.params.hdrdir, hdrDir); + initFromString(global.params.hdrname, hdrFile); + global.params.doHdrGeneration |= + global.params.hdrdir || global.params.hdrname; + + initFromString(global.params.moduleDepsFile, moduleDepsFile); + if (global.params.moduleDepsFile != NULL) { + global.params.moduleDeps = new OutBuffer; + } + + processVersions(debugArgs, "debug", DebugCondition::setGlobalLevel, + DebugCondition::addGlobalIdent); + processVersions(versions, "version", VersionCondition::setGlobalLevel, + VersionCondition::addGlobalIdent); + + global.params.output_o = + (opts::output_o == cl::BOU_UNSET && + !(opts::output_bc || opts::output_ll || opts::output_s)) + ? OUTPUTFLAGdefault + : opts::output_o == cl::BOU_TRUE ? OUTPUTFLAGset : OUTPUTFLAGno; + global.params.output_bc = opts::output_bc ? OUTPUTFLAGset : OUTPUTFLAGno; + global.params.output_ll = opts::output_ll ? OUTPUTFLAGset : OUTPUTFLAGno; + global.params.output_s = opts::output_s ? OUTPUTFLAGset : OUTPUTFLAGno; + + global.params.cov = (global.params.covPercent <= 100); + + templateLinkage = opts::linkonceTemplates ? LLGlobalValue::LinkOnceODRLinkage + : LLGlobalValue::WeakODRLinkage; + + if (global.params.run || !runargs.empty()) { + // FIXME: how to properly detect the presence of a PositionalEatsArgs + // option without parameters? We want to emit an error in that case... + // You'd think getNumOccurrences would do it, but it just returns the + // number of parameters) + // NOTE: Hacked around it by detecting -run in getenv_setargv(), where + // we're looking for it anyway, and pre-setting the flag... + global.params.run = true; + if (!runargs.empty()) { + char const *name = runargs[0].c_str(); + char const *ext = FileName::ext(name); + if (ext && FileName::equals(ext, "d") == 0 && + FileName::equals(ext, "di") == 0) { + error(Loc(), "-run must be followed by a source file, not '%s'", name); + } + + sourceFiles.push(mem.xstrdup(name)); + runargs.erase(runargs.begin()); + } else { + global.params.run = false; + error(Loc(), "Expected at least one argument to '-run'\n"); } + } - // Negated options - global.params.link = !compileOnly; - global.params.obj = !dontWriteObj; - global.params.useInlineAsm = !noAsm; - - // String options: std::string --> char* - initFromString(global.params.objname, objectFile); - initFromString(global.params.objdir, objectDir); - - initFromString(global.params.docdir, ddocDir); - initFromString(global.params.docname, ddocFile); - global.params.doDocComments |= - global.params.docdir || global.params.docname; - - initFromString(global.params.jsonfilename, jsonFile); - if (global.params.jsonfilename) - global.params.doJsonGeneration = true; - - initFromString(global.params.hdrdir, hdrDir); - initFromString(global.params.hdrname, hdrFile); - global.params.doHdrGeneration |= - global.params.hdrdir || global.params.hdrname; - - initFromString(global.params.moduleDepsFile, moduleDepsFile); - if (global.params.moduleDepsFile != NULL) - { - global.params.moduleDeps = new OutBuffer; - } - - processVersions(debugArgs, "debug", - DebugCondition::setGlobalLevel, - DebugCondition::addGlobalIdent); - processVersions(versions, "version", - VersionCondition::setGlobalLevel, - VersionCondition::addGlobalIdent); - - global.params.output_o = - (opts::output_o == cl::BOU_UNSET - && !(opts::output_bc || opts::output_ll || opts::output_s)) - ? OUTPUTFLAGdefault - : opts::output_o == cl::BOU_TRUE - ? OUTPUTFLAGset - : OUTPUTFLAGno; - global.params.output_bc = opts::output_bc ? OUTPUTFLAGset : OUTPUTFLAGno; - global.params.output_ll = opts::output_ll ? OUTPUTFLAGset : OUTPUTFLAGno; - global.params.output_s = opts::output_s ? OUTPUTFLAGset : OUTPUTFLAGno; - - global.params.cov = (global.params.covPercent <= 100); - - templateLinkage = - opts::linkonceTemplates ? LLGlobalValue::LinkOnceODRLinkage - : LLGlobalValue::WeakODRLinkage; - - if (global.params.run || !runargs.empty()) { - // FIXME: how to properly detect the presence of a PositionalEatsArgs - // option without parameters? We want to emit an error in that case... - // You'd think getNumOccurrences would do it, but it just returns the - // number of parameters) - // NOTE: Hacked around it by detecting -run in getenv_setargv(), where - // we're looking for it anyway, and pre-setting the flag... - global.params.run = true; - if (!runargs.empty()) { - char const * name = runargs[0].c_str(); - char const * ext = FileName::ext(name); - if (ext && FileName::equals(ext, "d") == 0 && - FileName::equals(ext, "di") == 0) { - error(Loc(), "-run must be followed by a source file, not '%s'", name); - } - - sourceFiles.push(mem.xstrdup(name)); - runargs.erase(runargs.begin()); - } else { - global.params.run = false; - error(Loc(), "Expected at least one argument to '-run'\n"); - } - } - - sourceFiles.reserve(fileList.size()); - for (const auto& file : fileList) - { - if (!file.empty()) - { - char* copy = mem.xstrdup(file.c_str()); + sourceFiles.reserve(fileList.size()); + for (const auto &file : fileList) { + if (!file.empty()) { + char *copy = mem.xstrdup(file.c_str()); #ifdef _WIN32 - std::replace(copy, copy + file.length(), '/', '\\'); + std::replace(copy, copy + file.length(), '/', '\\'); #endif - sourceFiles.push(copy); - } + sourceFiles.push(copy); } + } - 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."); + 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 { + // Parse comma-separated default library list. + std::stringstream libNames(linkDebugLib ? debugLib : defaultLib); + while (libNames.good()) { + std::string lib; + std::getline(libNames, lib, ','); + if (lib.empty()) + continue; + + char *arg = static_cast(mem.xmalloc(lib.size() + 3)); + strcpy(arg, "-l"); + strcpy(arg + 2, lib.c_str()); + global.params.linkswitches->push(arg); } - else - { - // Parse comma-separated default library list. - std::stringstream libNames(linkDebugLib ? debugLib : defaultLib); - while (libNames.good()) - { - std::string lib; - std::getline(libNames, lib, ','); - if (lib.empty()) continue; + } - char *arg = static_cast(mem.xmalloc(lib.size() + 3)); - strcpy(arg, "-l"); - strcpy(arg+2, lib.c_str()); - global.params.linkswitches->push(arg); - } + if (global.params.useUnitTests) + global.params.useAssert = 1; + + // -release downgrades default bounds checking level to BOUNDSCHECKsafeonly + // (only for safe functions). + global.params.useArrayBounds = + opts::nonSafeBoundsChecks ? BOUNDSCHECKon : BOUNDSCHECKsafeonly; + if (opts::boundsCheck != BOUNDSCHECKdefault) + global.params.useArrayBounds = opts::boundsCheck; + + // LDC output determination + + // if we don't link, autodetect target from extension + if (!global.params.link && !createStaticLib && global.params.objname) { + const char *ext = FileName::ext(global.params.objname); + bool autofound = false; + if (!ext) { + // keep things as they are + } else if (strcmp(ext, global.ll_ext) == 0) { + global.params.output_ll = OUTPUTFLAGset; + autofound = true; + } else if (strcmp(ext, global.bc_ext) == 0) { + global.params.output_bc = OUTPUTFLAGset; + autofound = true; + } else if (strcmp(ext, global.s_ext) == 0) { + global.params.output_s = OUTPUTFLAGset; + autofound = true; + } else if (strcmp(ext, global.obj_ext) == 0 || + strcmp(ext, global.obj_ext_alt) == 0) { + global.params.output_o = OUTPUTFLAGset; + autofound = true; + } else { + // append dot, so forceExt won't change existing name even if it contains + // dots + size_t len = strlen(global.params.objname); + char *s = static_cast(mem.xmalloc(len + 1 + 1)); + memcpy(s, global.params.objname, len); + s[len] = '.'; + s[len + 1] = 0; + global.params.objname = s; } + if (autofound && global.params.output_o == OUTPUTFLAGdefault) + global.params.output_o = OUTPUTFLAGno; + } - if (global.params.useUnitTests) - global.params.useAssert = 1; + // only link if possible + if (!global.params.obj || !global.params.output_o || createStaticLib) + global.params.link = 0; - // -release downgrades default bounds checking level to BOUNDSCHECKsafeonly (only for safe functions). - global.params.useArrayBounds = opts::nonSafeBoundsChecks ? BOUNDSCHECKon : BOUNDSCHECKsafeonly; - if (opts::boundsCheck != BOUNDSCHECKdefault) - global.params.useArrayBounds = opts::boundsCheck; + if (createStaticLib && createSharedLib) + error(Loc(), "-lib and -shared switches cannot be used together"); - // LDC output determination + if (createSharedLib && mRelocModel == llvm::Reloc::Default) + mRelocModel = llvm::Reloc::PIC_; - // if we don't link, autodetect target from extension - if(!global.params.link && !createStaticLib && global.params.objname) { - const char *ext = FileName::ext(global.params.objname); - bool autofound = false; - if (!ext) { - // keep things as they are - } else if (strcmp(ext, global.ll_ext) == 0) { - global.params.output_ll = OUTPUTFLAGset; - autofound = true; - } else if (strcmp(ext, global.bc_ext) == 0) { - global.params.output_bc = OUTPUTFLAGset; - autofound = true; - } else if (strcmp(ext, global.s_ext) == 0) { - global.params.output_s = OUTPUTFLAGset; - autofound = true; - } else if (strcmp(ext, global.obj_ext) == 0 || strcmp(ext, global.obj_ext_alt) == 0) { - global.params.output_o = OUTPUTFLAGset; - autofound = true; - } else { - // append dot, so forceExt won't change existing name even if it contains dots - size_t len = strlen(global.params.objname); - char* s = static_cast(mem.xmalloc(len + 1 + 1)); - memcpy(s, global.params.objname, len); - s[len] = '.'; - s[len+1] = 0; - global.params.objname = s; - - } - if(autofound && global.params.output_o == OUTPUTFLAGdefault) - global.params.output_o = OUTPUTFLAGno; + if (global.params.link && !createSharedLib) { + global.params.exefile = global.params.objname; + if (sourceFiles.dim > 1) + global.params.objname = NULL; + } else if (global.params.run) { + error(Loc(), "flags conflict with -run"); + } else if (global.params.objname && sourceFiles.dim > 1) { + if (!(createStaticLib || createSharedLib) && !singleObj) { + error(Loc(), "multiple source files, but only one .obj name"); } + } - // only link if possible - if (!global.params.obj || !global.params.output_o || createStaticLib) - global.params.link = 0; - - if (createStaticLib && createSharedLib) - error(Loc(), "-lib and -shared switches cannot be used together"); - - if (createSharedLib && mRelocModel == llvm::Reloc::Default) - mRelocModel = llvm::Reloc::PIC_; - - if (global.params.link && !createSharedLib) - { - global.params.exefile = global.params.objname; - if (sourceFiles.dim > 1) - global.params.objname = NULL; - } - else if (global.params.run) - { - error(Loc(), "flags conflict with -run"); - } - else if (global.params.objname && sourceFiles.dim > 1) { - if (!(createStaticLib || createSharedLib) && !singleObj) - { - error(Loc(), "multiple source files, but only one .obj name"); - } - } - - if (soname.getNumOccurrences() > 0 && !createSharedLib) { - error(Loc(), "-soname can be used only when building a shared library"); - } + if (soname.getNumOccurrences() > 0 && !createSharedLib) { + error(Loc(), "-soname can be used only when building a shared library"); + } } static void initializePasses() { - using namespace llvm; - // Initialize passes - PassRegistry &Registry = *PassRegistry::getPassRegistry(); - initializeCore(Registry); + using namespace llvm; + // Initialize passes + PassRegistry &Registry = *PassRegistry::getPassRegistry(); + initializeCore(Registry); #if LDC_LLVM_VER < 306 - initializeDebugIRPass(Registry); + initializeDebugIRPass(Registry); #endif - initializeScalarOpts(Registry); - initializeVectorization(Registry); - initializeIPO(Registry); - initializeAnalysis(Registry); + initializeScalarOpts(Registry); + initializeVectorization(Registry); + initializeIPO(Registry); + initializeAnalysis(Registry); #if LDC_LLVM_VER < 308 - initializeIPA(Registry); + initializeIPA(Registry); #endif - initializeTransformUtils(Registry); - initializeInstCombine(Registry); - initializeInstrumentation(Registry); - initializeTarget(Registry); - // For codegen passes, only passes that do IR to IR transformation are - // supported. For now, just add CodeGenPrepare. - initializeCodeGenPreparePass(Registry); + initializeTransformUtils(Registry); + initializeInstCombine(Registry); + initializeInstrumentation(Registry); + initializeTarget(Registry); + // For codegen passes, only passes that do IR to IR transformation are + // supported. For now, just add CodeGenPrepare. + initializeCodeGenPreparePass(Registry); #if LDC_LLVM_VER >= 306 - initializeAtomicExpandPass(Registry); - initializeRewriteSymbolsPass(Registry); + initializeAtomicExpandPass(Registry); + initializeRewriteSymbolsPass(Registry); #else - initializeAtomicExpandLoadLinkedPass(Registry); + initializeAtomicExpandLoadLinkedPass(Registry); #endif } /// Register the MIPS ABI. -static void registerMipsABI() -{ - switch (getMipsABI()) - { - case MipsABI::EABI: - VersionCondition::addPredefinedGlobalIdent("MIPS_EABI"); - break; - case MipsABI::O32: - VersionCondition::addPredefinedGlobalIdent("MIPS_O32"); - break; - case MipsABI::N32: - VersionCondition::addPredefinedGlobalIdent("MIPS_N32"); - break; - case MipsABI::N64: - VersionCondition::addPredefinedGlobalIdent("MIPS_N64"); - break; - case MipsABI::Unknown: - break; - } +static void registerMipsABI() { + switch (getMipsABI()) { + case MipsABI::EABI: + VersionCondition::addPredefinedGlobalIdent("MIPS_EABI"); + break; + case MipsABI::O32: + VersionCondition::addPredefinedGlobalIdent("MIPS_O32"); + break; + case MipsABI::N32: + VersionCondition::addPredefinedGlobalIdent("MIPS_N32"); + break; + case MipsABI::N64: + VersionCondition::addPredefinedGlobalIdent("MIPS_N64"); + break; + case MipsABI::Unknown: + break; + } } /// Register the float ABI. /// Also defines D_HardFloat or D_SoftFloat depending if FPU should be used -static void registerPredefinedFloatABI(const char *soft, const char *hard, const char *softfp=NULL) -{ - // Use target floating point unit instead of s/w float routines +static void registerPredefinedFloatABI(const char *soft, const char *hard, + const char *softfp = NULL) { +// Use target floating point unit instead of s/w float routines #if LDC_LLVM_VER >= 307 - // FIXME: This is a semantic change! - bool useFPU = gTargetMachine->Options.FloatABIType == llvm::FloatABI::Hard; + // FIXME: This is a semantic change! + bool useFPU = gTargetMachine->Options.FloatABIType == llvm::FloatABI::Hard; #else - bool useFPU = !gTargetMachine->Options.UseSoftFloat; + bool useFPU = !gTargetMachine->Options.UseSoftFloat; #endif - VersionCondition::addPredefinedGlobalIdent(useFPU ? "D_HardFloat" : "D_SoftFloat"); + VersionCondition::addPredefinedGlobalIdent(useFPU ? "D_HardFloat" + : "D_SoftFloat"); - if (gTargetMachine->Options.FloatABIType == llvm::FloatABI::Soft) - { - VersionCondition::addPredefinedGlobalIdent(useFPU && softfp ? softfp : soft); - } - else if (gTargetMachine->Options.FloatABIType == llvm::FloatABI::Hard) - { - assert(useFPU && "Should be using the FPU if using float-abi=hard"); - VersionCondition::addPredefinedGlobalIdent(hard); - } - else - { - assert(0 && "FloatABIType neither Soft or Hard"); - } + if (gTargetMachine->Options.FloatABIType == llvm::FloatABI::Soft) { + VersionCondition::addPredefinedGlobalIdent(useFPU && softfp ? softfp + : soft); + } else if (gTargetMachine->Options.FloatABIType == llvm::FloatABI::Hard) { + assert(useFPU && "Should be using the FPU if using float-abi=hard"); + VersionCondition::addPredefinedGlobalIdent(hard); + } else { + assert(0 && "FloatABIType neither Soft or Hard"); + } } /// Registers the predefined versions specific to the current target triple /// and other target specific options with VersionCondition. static void registerPredefinedTargetVersions() { - switch (global.params.targetTriple.getArch()) - { - case llvm::Triple::x86: - VersionCondition::addPredefinedGlobalIdent("X86"); - if (global.params.useInlineAsm) { - VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86"); - } - VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); - break; - case llvm::Triple::x86_64: - VersionCondition::addPredefinedGlobalIdent("X86_64"); - if (global.params.useInlineAsm) { - VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86_64"); - } - VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); - break; - case llvm::Triple::ppc: - VersionCondition::addPredefinedGlobalIdent("PPC"); - registerPredefinedFloatABI("PPC_SoftFloat", "PPC_HardFloat"); - break; - case llvm::Triple::ppc64: - case llvm::Triple::ppc64le: - VersionCondition::addPredefinedGlobalIdent("PPC64"); - registerPredefinedFloatABI("PPC_SoftFloat", "PPC_HardFloat"); - if (global.params.targetTriple.getOS() == llvm::Triple::Linux) - VersionCondition::addPredefinedGlobalIdent(global.params.targetTriple.getArch() == llvm::Triple::ppc64 - ? "ELFv1" : "ELFv2"); - break; - case llvm::Triple::arm: - case llvm::Triple::armeb: - VersionCondition::addPredefinedGlobalIdent("ARM"); - registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP"); - break; - case llvm::Triple::thumb: - VersionCondition::addPredefinedGlobalIdent("ARM"); - VersionCondition::addPredefinedGlobalIdent("Thumb"); // For backwards compatibility. - VersionCondition::addPredefinedGlobalIdent("ARM_Thumb"); - registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP"); - break; + switch (global.params.targetTriple.getArch()) { + case llvm::Triple::x86: + VersionCondition::addPredefinedGlobalIdent("X86"); + if (global.params.useInlineAsm) { + VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86"); + } + VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); + break; + case llvm::Triple::x86_64: + VersionCondition::addPredefinedGlobalIdent("X86_64"); + if (global.params.useInlineAsm) { + VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86_64"); + } + VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); + break; + case llvm::Triple::ppc: + VersionCondition::addPredefinedGlobalIdent("PPC"); + registerPredefinedFloatABI("PPC_SoftFloat", "PPC_HardFloat"); + break; + case llvm::Triple::ppc64: + case llvm::Triple::ppc64le: + VersionCondition::addPredefinedGlobalIdent("PPC64"); + registerPredefinedFloatABI("PPC_SoftFloat", "PPC_HardFloat"); + if (global.params.targetTriple.getOS() == llvm::Triple::Linux) + VersionCondition::addPredefinedGlobalIdent( + global.params.targetTriple.getArch() == llvm::Triple::ppc64 + ? "ELFv1" + : "ELFv2"); + break; + case llvm::Triple::arm: + case llvm::Triple::armeb: + VersionCondition::addPredefinedGlobalIdent("ARM"); + registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP"); + break; + case llvm::Triple::thumb: + VersionCondition::addPredefinedGlobalIdent("ARM"); + VersionCondition::addPredefinedGlobalIdent( + "Thumb"); // For backwards compatibility. + VersionCondition::addPredefinedGlobalIdent("ARM_Thumb"); + registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP"); + break; #if LDC_LLVM_VER == 305 - case llvm::Triple::arm64: - case llvm::Triple::arm64_be: + case llvm::Triple::arm64: + case llvm::Triple::arm64_be: #endif - case llvm::Triple::aarch64: - case llvm::Triple::aarch64_be: - VersionCondition::addPredefinedGlobalIdent("AArch64"); - registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP"); - break; - case llvm::Triple::mips: - case llvm::Triple::mipsel: - VersionCondition::addPredefinedGlobalIdent("MIPS"); - registerPredefinedFloatABI("MIPS_SoftFloat", "MIPS_HardFloat"); - registerMipsABI(); - break; - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - VersionCondition::addPredefinedGlobalIdent("MIPS64"); - registerPredefinedFloatABI("MIPS_SoftFloat", "MIPS_HardFloat"); - registerMipsABI(); - break; - case llvm::Triple::sparc: - // FIXME: Detect SPARC v8+ (SPARC_V8Plus). - VersionCondition::addPredefinedGlobalIdent("SPARC"); - registerPredefinedFloatABI("SPARC_SoftFloat", "SPARC_HardFloat"); - break; - case llvm::Triple::sparcv9: - VersionCondition::addPredefinedGlobalIdent("SPARC64"); - registerPredefinedFloatABI("SPARC_SoftFloat", "SPARC_HardFloat"); - break; - case llvm::Triple::nvptx: - VersionCondition::addPredefinedGlobalIdent("NVPTX"); - VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); - break; - case llvm::Triple::nvptx64: - VersionCondition::addPredefinedGlobalIdent("NVPTX64"); - VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); - break; - case llvm::Triple::systemz: - VersionCondition::addPredefinedGlobalIdent("SystemZ"); - VersionCondition::addPredefinedGlobalIdent("S390X"); // For backwards compatibility. - VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); - break; - default: - error(Loc(), "invalid cpu architecture specified: %s", global.params.targetTriple.getArchName().str().c_str()); - fatal(); - } + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_be: + VersionCondition::addPredefinedGlobalIdent("AArch64"); + registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP"); + break; + case llvm::Triple::mips: + case llvm::Triple::mipsel: + VersionCondition::addPredefinedGlobalIdent("MIPS"); + registerPredefinedFloatABI("MIPS_SoftFloat", "MIPS_HardFloat"); + registerMipsABI(); + break; + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + VersionCondition::addPredefinedGlobalIdent("MIPS64"); + registerPredefinedFloatABI("MIPS_SoftFloat", "MIPS_HardFloat"); + registerMipsABI(); + break; + case llvm::Triple::sparc: + // FIXME: Detect SPARC v8+ (SPARC_V8Plus). + VersionCondition::addPredefinedGlobalIdent("SPARC"); + registerPredefinedFloatABI("SPARC_SoftFloat", "SPARC_HardFloat"); + break; + case llvm::Triple::sparcv9: + VersionCondition::addPredefinedGlobalIdent("SPARC64"); + registerPredefinedFloatABI("SPARC_SoftFloat", "SPARC_HardFloat"); + break; + case llvm::Triple::nvptx: + VersionCondition::addPredefinedGlobalIdent("NVPTX"); + VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); + break; + case llvm::Triple::nvptx64: + VersionCondition::addPredefinedGlobalIdent("NVPTX64"); + VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); + break; + case llvm::Triple::systemz: + VersionCondition::addPredefinedGlobalIdent("SystemZ"); + VersionCondition::addPredefinedGlobalIdent( + "S390X"); // For backwards compatibility. + VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); + break; + default: + error(Loc(), "invalid cpu architecture specified: %s", + global.params.targetTriple.getArchName().str().c_str()); + fatal(); + } - // endianness - if (gDataLayout->isLittleEndian()) { - VersionCondition::addPredefinedGlobalIdent("LittleEndian"); - } - else { - VersionCondition::addPredefinedGlobalIdent("BigEndian"); - } + // endianness + if (gDataLayout->isLittleEndian()) { + VersionCondition::addPredefinedGlobalIdent("LittleEndian"); + } else { + VersionCondition::addPredefinedGlobalIdent("BigEndian"); + } - // a generic 64bit version - if (global.params.isLP64) { - VersionCondition::addPredefinedGlobalIdent("D_LP64"); - } + // a generic 64bit version + if (global.params.isLP64) { + VersionCondition::addPredefinedGlobalIdent("D_LP64"); + } - if (gTargetMachine->getRelocationModel() == llvm::Reloc::PIC_) { - VersionCondition::addPredefinedGlobalIdent("D_PIC"); - } + if (gTargetMachine->getRelocationModel() == llvm::Reloc::PIC_) { + VersionCondition::addPredefinedGlobalIdent("D_PIC"); + } - // parse the OS out of the target triple - // see http://gcc.gnu.org/install/specific.html for details - // also llvm's different SubTargets have useful information - switch (global.params.targetTriple.getOS()) - { - case llvm::Triple::Win32: - VersionCondition::addPredefinedGlobalIdent("Windows"); - VersionCondition::addPredefinedGlobalIdent(global.params.is64bit ? "Win64" : "Win32"); - if (global.params.targetTriple.isKnownWindowsMSVCEnvironment()) - { - VersionCondition::addPredefinedGlobalIdent("CRuntime_Microsoft"); - } - if (global.params.targetTriple.isWindowsGNUEnvironment()) - { - VersionCondition::addPredefinedGlobalIdent("mingw32"); // For backwards compatibility. - VersionCondition::addPredefinedGlobalIdent("MinGW"); - } - if (global.params.targetTriple.isWindowsCygwinEnvironment()) - { - error(Loc(), "Cygwin is not yet supported"); - fatal(); - VersionCondition::addPredefinedGlobalIdent("Cygwin"); - } - break; - case llvm::Triple::Linux: - if (global.params.targetTriple.getEnvironment() == llvm::Triple::Android) - { - VersionCondition::addPredefinedGlobalIdent("Android"); - VersionCondition::addPredefinedGlobalIdent("CRuntime_Bionic"); - } - else - { - VersionCondition::addPredefinedGlobalIdent("linux"); - VersionCondition::addPredefinedGlobalIdent("Posix"); - VersionCondition::addPredefinedGlobalIdent("CRuntime_Glibc"); - } - break; - case llvm::Triple::Haiku: - VersionCondition::addPredefinedGlobalIdent("Haiku"); - VersionCondition::addPredefinedGlobalIdent("Posix"); - break; - case llvm::Triple::Darwin: - VersionCondition::addPredefinedGlobalIdent("OSX"); - VersionCondition::addPredefinedGlobalIdent("darwin"); // For backwards compatibility. - VersionCondition::addPredefinedGlobalIdent("Posix"); - break; - case llvm::Triple::FreeBSD: - VersionCondition::addPredefinedGlobalIdent("FreeBSD"); - VersionCondition::addPredefinedGlobalIdent("Posix"); - break; - case llvm::Triple::Solaris: - VersionCondition::addPredefinedGlobalIdent("Solaris"); - VersionCondition::addPredefinedGlobalIdent("Posix"); - break; - case llvm::Triple::DragonFly: - VersionCondition::addPredefinedGlobalIdent("DragonFlyBSD"); - VersionCondition::addPredefinedGlobalIdent("Posix"); - break; - case llvm::Triple::NetBSD: - VersionCondition::addPredefinedGlobalIdent("NetBSD"); - VersionCondition::addPredefinedGlobalIdent("Posix"); - break; - case llvm::Triple::OpenBSD: - VersionCondition::addPredefinedGlobalIdent("OpenBSD"); - VersionCondition::addPredefinedGlobalIdent("Posix"); - break; - case llvm::Triple::AIX: - VersionCondition::addPredefinedGlobalIdent("AIX"); - VersionCondition::addPredefinedGlobalIdent("Posix"); - break; - default: - switch (global.params.targetTriple.getEnvironment()) - { - case llvm::Triple::Android: - VersionCondition::addPredefinedGlobalIdent("Android"); - break; - default: - error(Loc(), "target '%s' is not yet supported", global.params.targetTriple.str().c_str()); - fatal(); - } + // parse the OS out of the target triple + // see http://gcc.gnu.org/install/specific.html for details + // also llvm's different SubTargets have useful information + switch (global.params.targetTriple.getOS()) { + case llvm::Triple::Win32: + VersionCondition::addPredefinedGlobalIdent("Windows"); + VersionCondition::addPredefinedGlobalIdent(global.params.is64bit ? "Win64" + : "Win32"); + if (global.params.targetTriple.isKnownWindowsMSVCEnvironment()) { + VersionCondition::addPredefinedGlobalIdent("CRuntime_Microsoft"); } + if (global.params.targetTriple.isWindowsGNUEnvironment()) { + VersionCondition::addPredefinedGlobalIdent( + "mingw32"); // For backwards compatibility. + VersionCondition::addPredefinedGlobalIdent("MinGW"); + } + if (global.params.targetTriple.isWindowsCygwinEnvironment()) { + error(Loc(), "Cygwin is not yet supported"); + fatal(); + VersionCondition::addPredefinedGlobalIdent("Cygwin"); + } + break; + case llvm::Triple::Linux: + if (global.params.targetTriple.getEnvironment() == llvm::Triple::Android) { + VersionCondition::addPredefinedGlobalIdent("Android"); + VersionCondition::addPredefinedGlobalIdent("CRuntime_Bionic"); + } else { + VersionCondition::addPredefinedGlobalIdent("linux"); + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("CRuntime_Glibc"); + } + break; + case llvm::Triple::Haiku: + VersionCondition::addPredefinedGlobalIdent("Haiku"); + VersionCondition::addPredefinedGlobalIdent("Posix"); + break; + case llvm::Triple::Darwin: + VersionCondition::addPredefinedGlobalIdent("OSX"); + VersionCondition::addPredefinedGlobalIdent( + "darwin"); // For backwards compatibility. + VersionCondition::addPredefinedGlobalIdent("Posix"); + break; + case llvm::Triple::FreeBSD: + VersionCondition::addPredefinedGlobalIdent("FreeBSD"); + VersionCondition::addPredefinedGlobalIdent("Posix"); + break; + case llvm::Triple::Solaris: + VersionCondition::addPredefinedGlobalIdent("Solaris"); + VersionCondition::addPredefinedGlobalIdent("Posix"); + break; + case llvm::Triple::DragonFly: + VersionCondition::addPredefinedGlobalIdent("DragonFlyBSD"); + VersionCondition::addPredefinedGlobalIdent("Posix"); + break; + case llvm::Triple::NetBSD: + VersionCondition::addPredefinedGlobalIdent("NetBSD"); + VersionCondition::addPredefinedGlobalIdent("Posix"); + break; + case llvm::Triple::OpenBSD: + VersionCondition::addPredefinedGlobalIdent("OpenBSD"); + VersionCondition::addPredefinedGlobalIdent("Posix"); + break; + case llvm::Triple::AIX: + VersionCondition::addPredefinedGlobalIdent("AIX"); + VersionCondition::addPredefinedGlobalIdent("Posix"); + break; + default: + switch (global.params.targetTriple.getEnvironment()) { + case llvm::Triple::Android: + VersionCondition::addPredefinedGlobalIdent("Android"); + break; + default: + error(Loc(), "target '%s' is not yet supported", + global.params.targetTriple.str().c_str()); + fatal(); + } + } } /// Registers all predefined D version identifiers for the current /// configuration with VersionCondition. static void registerPredefinedVersions() { - VersionCondition::addPredefinedGlobalIdent("LDC"); - VersionCondition::addPredefinedGlobalIdent("all"); - VersionCondition::addPredefinedGlobalIdent("D_Version2"); + VersionCondition::addPredefinedGlobalIdent("LDC"); + VersionCondition::addPredefinedGlobalIdent("all"); + VersionCondition::addPredefinedGlobalIdent("D_Version2"); - if (global.params.doDocComments) - VersionCondition::addPredefinedGlobalIdent("D_Ddoc"); + if (global.params.doDocComments) + VersionCondition::addPredefinedGlobalIdent("D_Ddoc"); - if (global.params.useUnitTests) - VersionCondition::addPredefinedGlobalIdent("unittest"); + if (global.params.useUnitTests) + VersionCondition::addPredefinedGlobalIdent("unittest"); - if (global.params.useAssert) - VersionCondition::addPredefinedGlobalIdent("assert"); + if (global.params.useAssert) + VersionCondition::addPredefinedGlobalIdent("assert"); - if (global.params.useArrayBounds == BOUNDSCHECKoff) - VersionCondition::addPredefinedGlobalIdent("D_NoBoundsChecks"); + if (global.params.useArrayBounds == BOUNDSCHECKoff) + VersionCondition::addPredefinedGlobalIdent("D_NoBoundsChecks"); - registerPredefinedTargetVersions(); + registerPredefinedTargetVersions(); - // Pass sanitizer arguments to linker. Requires clang. - if (opts::sanitize == opts::AddressSanitizer) { - VersionCondition::addPredefinedGlobalIdent("LDC_AddressSanitizer"); - } + // Pass sanitizer arguments to linker. Requires clang. + if (opts::sanitize == opts::AddressSanitizer) { + VersionCondition::addPredefinedGlobalIdent("LDC_AddressSanitizer"); + } - if (opts::sanitize == opts::MemorySanitizer) { - VersionCondition::addPredefinedGlobalIdent("LDC_MemorySanitizer"); - } + if (opts::sanitize == opts::MemorySanitizer) { + VersionCondition::addPredefinedGlobalIdent("LDC_MemorySanitizer"); + } - if (opts::sanitize == opts::ThreadSanitizer) { - VersionCondition::addPredefinedGlobalIdent("LDC_ThreadSanitizer"); - } + if (opts::sanitize == opts::ThreadSanitizer) { + VersionCondition::addPredefinedGlobalIdent("LDC_ThreadSanitizer"); + } - // Expose LLVM version to runtime +// Expose LLVM version to runtime #define STR(x) #x #define XSTR(x) STR(x) - VersionCondition::addPredefinedGlobalIdent("LDC_LLVM_" XSTR(LDC_LLVM_VER)); + VersionCondition::addPredefinedGlobalIdent("LDC_LLVM_" XSTR(LDC_LLVM_VER)); #undef XSTR #undef STR } /// Dump all predefined version identifiers. -static void dumpPredefinedVersions() -{ - if (global.params.verbose && global.params.versionids) - { - fprintf(global.stdmsg, "predefs "); - int col = 10; - for (auto id : *global.params.versionids) - { - int len = strlen(id) + 1; - if (col + len > 80) - { - col = 10; - fprintf(global.stdmsg, "\n "); - } - col += len; - fprintf(global.stdmsg, " %s", id); - } - fprintf(global.stdmsg, "\n"); +static void dumpPredefinedVersions() { + if (global.params.verbose && global.params.versionids) { + fprintf(global.stdmsg, "predefs "); + int col = 10; + for (auto id : *global.params.versionids) { + int len = strlen(id) + 1; + if (col + len > 80) { + col = 10; + fprintf(global.stdmsg, "\n "); + } + col += len; + fprintf(global.stdmsg, " %s", id); } + fprintf(global.stdmsg, "\n"); + } } /// Emits the .json AST description file. /// /// This (ugly) piece of code has been taken from DMD's mars.c and should be /// kept in sync with the former. -static void emitJson(Modules &modules) -{ - OutBuffer buf; - json_generate(&buf, &modules); +static void emitJson(Modules &modules) { + OutBuffer buf; + json_generate(&buf, &modules); - // Write buf to file - const char *name = global.params.jsonfilename; + // Write buf to file + const char *name = global.params.jsonfilename; - if (name && name[0] == '-' && name[1] == 0) - { // Write to stdout; assume it succeeds - (void)fwrite(buf.data, 1, buf.offset, stdout); + if (name && name[0] == '-' && + name[1] == 0) { // Write to stdout; assume it succeeds + (void)fwrite(buf.data, 1, buf.offset, stdout); + } else { + /* The filename generation code here should be harmonized with + * Module::setOutfile() + */ + const char *jsonfilename; + + if (name && *name) { + jsonfilename = FileName::defaultExt(name, global.json_ext); + } else { + // Generate json file name from first obj name + const char *n = (*global.params.objfiles)[0]; + n = FileName::name(n); + + // if (!FileName::absolute(name)) + // name = FileName::combine(dir, name); + + jsonfilename = FileName::forceExt(n, global.json_ext); } - else - { - /* The filename generation code here should be harmonized with Module::setOutfile() - */ - const char *jsonfilename; - if (name && *name) - { - jsonfilename = FileName::defaultExt(name, global.json_ext); - } - else - { - // Generate json file name from first obj name - const char *n = (*global.params.objfiles)[0]; - n = FileName::name(n); + ensurePathToNameExists(Loc(), jsonfilename); - //if (!FileName::absolute(name)) - //name = FileName::combine(dir, name); + File *jsonfile = new File(jsonfilename); - jsonfilename = FileName::forceExt(n, global.json_ext); - } - - ensurePathToNameExists(Loc(), jsonfilename); - - File *jsonfile = new File(jsonfilename); - - jsonfile->setbuffer(buf.data, buf.offset); - jsonfile->ref = 1; - writeFile(Loc(), jsonfile); - } + jsonfile->setbuffer(buf.data, buf.offset); + jsonfile->ref = 1; + writeFile(Loc(), jsonfile); + } } +int main(int argc, char **argv) { + // stack trace on signals + llvm::sys::PrintStackTraceOnErrorSignal(); -int main(int argc, char **argv) -{ - // stack trace on signals - llvm::sys::PrintStackTraceOnErrorSignal(); + exe_path::initialize(argv[0], reinterpret_cast(main)); - exe_path::initialize(argv[0], reinterpret_cast(main)); + global.init(); + global.version = ldc::dmd_version; + global.ldc_version = ldc::ldc_version; + global.llvm_version = ldc::llvm_version; - global.init(); - global.version = ldc::dmd_version; - global.ldc_version = ldc::ldc_version; - global.llvm_version = ldc::llvm_version; + // Initialize LLVM before parsing the command line so that --version shows + // registered targets. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllAsmParsers(); - // Initialize LLVM before parsing the command line so that --version shows - // registered targets. - llvm::InitializeAllTargetInfos(); - llvm::InitializeAllTargets(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllAsmPrinters(); - llvm::InitializeAllAsmParsers(); + initializePasses(); - initializePasses(); + bool helpOnly; + Strings files; + parseCommandLine(argc, argv, files, helpOnly); - bool helpOnly; - Strings files; - parseCommandLine(argc, argv, files, helpOnly); + if (files.dim == 0 && !helpOnly) { + cl::PrintHelpMessage(); + return EXIT_FAILURE; + } - if (files.dim == 0 && !helpOnly) - { - cl::PrintHelpMessage(); - return EXIT_FAILURE; - } + if (global.errors) + fatal(); - if (global.errors) - fatal(); + // Set up the TargetMachine. + ExplicitBitness::Type bitness = ExplicitBitness::None; + if ((m32bits || m64bits) && (!mArch.empty() || !mTargetTriple.empty())) + error(Loc(), "-m32 and -m64 switches cannot be used together with -march " + "and -mtriple switches"); - // Set up the TargetMachine. - ExplicitBitness::Type bitness = ExplicitBitness::None; - if ((m32bits || m64bits) && (!mArch.empty() || !mTargetTriple.empty())) - error(Loc(), "-m32 and -m64 switches cannot be used together with -march and -mtriple switches"); + if (m32bits) + bitness = ExplicitBitness::M32; + if (m64bits) { + if (bitness != ExplicitBitness::None) + error(Loc(), "cannot use both -m32 and -m64 options"); + bitness = ExplicitBitness::M64; + } - if (m32bits) - bitness = ExplicitBitness::M32; - if (m64bits) - { - if (bitness != ExplicitBitness::None) - error(Loc(), "cannot use both -m32 and -m64 options"); - bitness = ExplicitBitness::M64; - } + if (global.errors) + fatal(); - if (global.errors) - fatal(); - - gTargetMachine = createTargetMachine(mTargetTriple, mArch, mCPU, mAttrs, - bitness, mFloatABI, mRelocModel, mCodeModel, codeGenOptLevel(), - global.params.symdebug || disableFpElim, disableLinkerStripDead); + gTargetMachine = createTargetMachine( + mTargetTriple, mArch, mCPU, mAttrs, bitness, mFloatABI, mRelocModel, + mCodeModel, codeGenOptLevel(), global.params.symdebug || disableFpElim, + disableLinkerStripDead); #if LDC_LLVM_VER >= 308 - static llvm::DataLayout DL = gTargetMachine->createDataLayout(); - gDataLayout = &DL; + static llvm::DataLayout DL = gTargetMachine->createDataLayout(); + gDataLayout = &DL; #elif LDC_LLVM_VER >= 307 - gDataLayout = gTargetMachine->getDataLayout(); + gDataLayout = gTargetMachine->getDataLayout(); #elif LDC_LLVM_VER >= 306 - gDataLayout = gTargetMachine->getSubtargetImpl()->getDataLayout(); + gDataLayout = gTargetMachine->getSubtargetImpl()->getDataLayout(); #else - gDataLayout = gTargetMachine->getDataLayout(); + gDataLayout = gTargetMachine->getDataLayout(); #endif - { - llvm::Triple triple = llvm::Triple(gTargetMachine->getTargetTriple()); - global.params.targetTriple = triple; - global.params.isLinux = triple.getOS() == llvm::Triple::Linux; - global.params.isOSX = triple.isMacOSX(); - global.params.isWindows = triple.isOSWindows(); - global.params.isFreeBSD = triple.getOS() == llvm::Triple::FreeBSD; - global.params.isOpenBSD = triple.getOS() == llvm::Triple::OpenBSD; - global.params.isSolaris = triple.getOS() == llvm::Triple::Solaris; - global.params.isLP64 = gDataLayout->getPointerSizeInBits() == 64; - global.params.is64bit = triple.isArch64Bit(); + { + llvm::Triple triple = llvm::Triple(gTargetMachine->getTargetTriple()); + global.params.targetTriple = triple; + global.params.isLinux = triple.getOS() == llvm::Triple::Linux; + global.params.isOSX = triple.isMacOSX(); + global.params.isWindows = triple.isOSWindows(); + global.params.isFreeBSD = triple.getOS() == llvm::Triple::FreeBSD; + global.params.isOpenBSD = triple.getOS() == llvm::Triple::OpenBSD; + global.params.isSolaris = triple.getOS() == llvm::Triple::Solaris; + global.params.isLP64 = gDataLayout->getPointerSizeInBits() == 64; + global.params.is64bit = triple.isArch64Bit(); + } + + // allocate the target abi + gABI = TargetABI::getTarget(); + + // Set predefined version identifiers. + registerPredefinedVersions(); + dumpPredefinedVersions(); + + if (global.params.targetTriple.isOSWindows()) { + global.dll_ext = "dll"; + global.lib_ext = "lib"; + } else { + global.dll_ext = "so"; + global.lib_ext = "a"; + } + + // Initialization + Lexer::initLexer(); + Type::init(); + Id::initialize(); + Module::init(); + Target::init(); + Expression::init(); + initPrecedence(); + builtin_init(); + initTraitsStringTable(); + + // Build import search path + if (global.params.imppath) { + for (unsigned i = 0; i < global.params.imppath->dim; i++) { + const char *path = + static_cast(global.params.imppath->data[i]); + Strings *a = FileName::splitPath(path); + + if (a) { + if (!global.path) + global.path = new Strings(); + global.path->append(a); + } } + } - // allocate the target abi - gABI = TargetABI::getTarget(); + // Build string import search path + if (global.params.fileImppath) { + for (unsigned i = 0; i < global.params.fileImppath->dim; i++) { + const char *path = + static_cast(global.params.fileImppath->data[i]); + Strings *a = FileName::splitPath(path); - // Set predefined version identifiers. - registerPredefinedVersions(); - dumpPredefinedVersions(); - - if (global.params.targetTriple.isOSWindows()) { - global.dll_ext = "dll"; - global.lib_ext = "lib"; - } else { - global.dll_ext = "so"; - global.lib_ext = "a"; + if (a) { + if (!global.filePath) + global.filePath = new Strings(); + global.filePath->append(a); + } } + } - // Initialization - Lexer::initLexer(); - Type::init(); - Id::initialize(); - Module::init(); - Target::init(); - Expression::init(); - initPrecedence(); - builtin_init(); - initTraitsStringTable(); + if (global.params.addMain) { + // a dummy name, we never actually look up this file + files.push(const_cast(global.main_d)); + } - // Build import search path - if (global.params.imppath) - { - for (unsigned i = 0; i < global.params.imppath->dim; i++) - { - const char *path = static_cast(global.params.imppath->data[i]); - Strings *a = FileName::splitPath(path); + // Create Modules + Modules modules; + modules.reserve(files.dim); + for (unsigned i = 0; i < files.dim; i++) { + Identifier *id; + const char *ext; + const char *name; - if (a) - { - if (!global.path) - global.path = new Strings(); - global.path->append(a); - } - } - } + const char *p = files.data[i]; - // Build string import search path - if (global.params.fileImppath) - { - for (unsigned i = 0; i < global.params.fileImppath->dim; i++) - { - const char *path = static_cast(global.params.fileImppath->data[i]); - Strings *a = FileName::splitPath(path); - - if (a) - { - if (!global.filePath) - global.filePath = new Strings(); - global.filePath->append(a); - } - } - } - - if (global.params.addMain) - { - // a dummy name, we never actually look up this file - files.push(const_cast(global.main_d)); - } - - // Create Modules - Modules modules; - modules.reserve(files.dim); - for (unsigned i = 0; i < files.dim; i++) - { Identifier *id; - const char *ext; - const char *name; - - const char *p = files.data[i]; - - p = FileName::name(p); // strip path - ext = FileName::ext(p); - if (ext) - { + p = FileName::name(p); // strip path + ext = FileName::ext(p); + if (ext) { #if LDC_POSIX - if (strcmp(ext, global.obj_ext) == 0 || - strcmp(ext, global.bc_ext) == 0) + if (strcmp(ext, global.obj_ext) == 0 || strcmp(ext, global.bc_ext) == 0) #else - if (Port::stricmp(ext, global.obj_ext) == 0 || - Port::stricmp(ext, global.obj_ext_alt) == 0 || - Port::stricmp(ext, global.bc_ext) == 0) + if (Port::stricmp(ext, global.obj_ext) == 0 || + Port::stricmp(ext, global.obj_ext_alt) == 0 || + Port::stricmp(ext, global.bc_ext) == 0) #endif - { - global.params.objfiles->push(static_cast(files.data[i])); - continue; - } + { + global.params.objfiles->push(static_cast(files.data[i])); + continue; + } #if LDC_POSIX - if (strcmp(ext, "a") == 0) + if (strcmp(ext, "a") == 0) #elif __MINGW32__ - if (Port::stricmp(ext, "a") == 0) + if (Port::stricmp(ext, "a") == 0) #else - if (Port::stricmp(ext, "lib") == 0) + if (Port::stricmp(ext, "lib") == 0) #endif - { - global.params.libfiles->push(static_cast(files.data[i])); - continue; - } + { + global.params.libfiles->push(static_cast(files.data[i])); + continue; + } - if (strcmp(ext, global.ddoc_ext) == 0) - { - global.params.ddocfiles->push(static_cast(files.data[i])); - continue; - } + if (strcmp(ext, global.ddoc_ext) == 0) { + global.params.ddocfiles->push(static_cast(files.data[i])); + continue; + } - if (FileName::equals(ext, global.json_ext)) - { - global.params.doJsonGeneration = 1; - global.params.jsonfilename = static_cast(files.data[i]); - continue; - } + if (FileName::equals(ext, global.json_ext)) { + global.params.doJsonGeneration = 1; + global.params.jsonfilename = static_cast(files.data[i]); + continue; + } #if !LDC_POSIX - if (Port::stricmp(ext, "res") == 0) - { - global.params.resfile = static_cast(files.data[i]); - continue; - } + if (Port::stricmp(ext, "res") == 0) { + global.params.resfile = static_cast(files.data[i]); + continue; + } - if (Port::stricmp(ext, "def") == 0) - { - global.params.deffile = static_cast(files.data[i]); - continue; - } + if (Port::stricmp(ext, "def") == 0) { + global.params.deffile = static_cast(files.data[i]); + continue; + } - if (Port::stricmp(ext, "exe") == 0) - { - global.params.exefile = static_cast(files.data[i]); - continue; - } + if (Port::stricmp(ext, "exe") == 0) { + global.params.exefile = static_cast(files.data[i]); + continue; + } #endif - if (Port::stricmp(ext, global.mars_ext) == 0 || - Port::stricmp(ext, global.hdr_ext) == 0 || - FileName::equals(ext, "dd")) - { - ext--; // skip onto '.' - assert(*ext == '.'); - char *tmp = static_cast(mem.xmalloc((ext - p) + 1)); - memcpy(tmp, p, ext - p); - tmp[ext - p] = 0; // strip extension - name = tmp; + if (Port::stricmp(ext, global.mars_ext) == 0 || + Port::stricmp(ext, global.hdr_ext) == 0 || + FileName::equals(ext, "dd")) { + ext--; // skip onto '.' + assert(*ext == '.'); + char *tmp = static_cast(mem.xmalloc((ext - p) + 1)); + memcpy(tmp, p, ext - p); + tmp[ext - p] = 0; // strip extension + name = tmp; - if (name[0] == 0 || - strcmp(name, "..") == 0 || - strcmp(name, ".") == 0) - { - goto Linvalid; - } - } - else - { error(Loc(), "unrecognized file extension %s\n", ext); - fatal(); - } + if (name[0] == 0 || strcmp(name, "..") == 0 || strcmp(name, ".") == 0) { + goto Linvalid; } - else - { name = p; - if (!*p) - { - Linvalid: - error(Loc(), "invalid file name '%s'", static_cast(files.data[i])); - fatal(); - } - name = p; - } - - id = Identifier::idPool(name); - Module *m = new Module(files.data[i], id, global.params.doDocComments, global.params.doHdrGeneration); - modules.push(m); - } - - // Read files, parse them - for (unsigned i = 0; i < modules.dim; i++) - { - Module *m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "parse %s\n", m->toChars()); - if (!Module::rootModule) - Module::rootModule = m; - m->importedFrom = m; - - if (strcmp(m->srcfile->name->str, global.main_d) == 0) - { - static const char buf[] = "void main(){}"; - m->srcfile->setbuffer(const_cast(buf), sizeof(buf)); - m->srcfile->ref = 1; - } - else - { - m->read(Loc()); - } - - m->parse(global.params.doDocComments); - m->buildTargetFiles(singleObj, createSharedLib || createStaticLib); - m->deleteObjFile(); - if (m->isDocFile) - { - gendocfile(m); - - // Remove m from list of modules - modules.remove(i); - i--; - } - } - if (global.errors) + } else { + error(Loc(), "unrecognized file extension %s\n", ext); fatal(); - - if (global.params.doHdrGeneration) - { - /* Generate 'header' import files. - * Since 'header' import files must be independent of command - * line switches and what else is imported, they are generated - * before any semantic analysis. - */ - for (unsigned i = 0; i < modules.dim; i++) - { - if (global.params.verbose) - fprintf(global.stdmsg, "import %s\n", modules[i]->toChars()); - genhdrfile(modules[i]); - } - } - if (global.errors) + } + } else { + name = p; + if (!*p) { + Linvalid: + error(Loc(), "invalid file name '%s'", + static_cast(files.data[i])); fatal(); - - // load all unconditional imports for better symbol resolving - for (unsigned i = 0; i < modules.dim; i++) - { - if (global.params.verbose) - fprintf(global.stdmsg, "importall %s\n", modules[i]->toChars()); - modules[i]->importAll(0); + } + name = p; } - if (global.errors) - fatal(); - // Do semantic analysis - for (unsigned i = 0; i < modules.dim; i++) - { - if (global.params.verbose) - fprintf(global.stdmsg, "semantic %s\n", modules[i]->toChars()); - modules[i]->semantic(); + id = Identifier::idPool(name); + Module *m = new Module(files.data[i], id, global.params.doDocComments, + global.params.doHdrGeneration); + modules.push(m); + } + + // Read files, parse them + for (unsigned i = 0; i < modules.dim; i++) { + Module *m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "parse %s\n", m->toChars()); + if (!Module::rootModule) + Module::rootModule = m; + m->importedFrom = m; + + if (strcmp(m->srcfile->name->str, global.main_d) == 0) { + static const char buf[] = "void main(){}"; + m->srcfile->setbuffer(const_cast(buf), sizeof(buf)); + m->srcfile->ref = 1; + } else { + m->read(Loc()); } - if (global.errors) + + m->parse(global.params.doDocComments); + m->buildTargetFiles(singleObj, createSharedLib || createStaticLib); + m->deleteObjFile(); + if (m->isDocFile) { + gendocfile(m); + + // Remove m from list of modules + modules.remove(i); + i--; + } + } + if (global.errors) + fatal(); + + if (global.params.doHdrGeneration) { + /* Generate 'header' import files. + * Since 'header' import files must be independent of command + * line switches and what else is imported, they are generated + * before any semantic analysis. + */ + for (unsigned i = 0; i < modules.dim; i++) { + if (global.params.verbose) + fprintf(global.stdmsg, "import %s\n", modules[i]->toChars()); + genhdrfile(modules[i]); + } + } + if (global.errors) + fatal(); + + // load all unconditional imports for better symbol resolving + for (unsigned i = 0; i < modules.dim; i++) { + if (global.params.verbose) + fprintf(global.stdmsg, "importall %s\n", modules[i]->toChars()); + modules[i]->importAll(0); + } + if (global.errors) + fatal(); + + // Do semantic analysis + for (unsigned i = 0; i < modules.dim; i++) { + if (global.params.verbose) + fprintf(global.stdmsg, "semantic %s\n", modules[i]->toChars()); + modules[i]->semantic(); + } + if (global.errors) + fatal(); + + Module::dprogress = 1; + Module::runDeferredSemantic(); + + // Do pass 2 semantic analysis + for (unsigned i = 0; i < modules.dim; i++) { + if (global.params.verbose) + fprintf(global.stdmsg, "semantic2 %s\n", modules[i]->toChars()); + modules[i]->semantic2(); + } + if (global.errors) + fatal(); + + // Do pass 3 semantic analysis + for (unsigned i = 0; i < modules.dim; i++) { + if (global.params.verbose) + fprintf(global.stdmsg, "semantic3 %s\n", modules[i]->toChars()); + modules[i]->semantic3(); + } + if (global.errors) + fatal(); + + Module::runDeferredSemantic3(); + + if (global.errors || global.warnings) + fatal(); + + // Now that we analyzed all modules, write the module dependency file if + // the user requested it. + if (global.params.moduleDepsFile != NULL) { + File deps(global.params.moduleDepsFile); + OutBuffer *ob = global.params.moduleDeps; + deps.setbuffer(static_cast(ob->data), ob->offset); + deps.write(); + } + + // Generate one or more object/IR/bitcode files. + if (global.params.obj && !modules.empty()) { + ldc::CodeGenerator cg(llvm::getGlobalContext(), singleObj); + + for (unsigned i = 0; i < modules.dim; i++) { + Module *const m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "code %s\n", m->toChars()); + + cg.emit(m); + + if (global.errors) fatal(); - - Module::dprogress = 1; - Module::runDeferredSemantic(); - - // Do pass 2 semantic analysis - for (unsigned i = 0; i < modules.dim; i++) - { - if (global.params.verbose) - fprintf(global.stdmsg, "semantic2 %s\n", modules[i]->toChars()); - modules[i]->semantic2(); } - if (global.errors) - fatal(); + } - // Do pass 3 semantic analysis - for (unsigned i = 0; i < modules.dim; i++) - { - if (global.params.verbose) - fprintf(global.stdmsg, "semantic3 %s\n", modules[i]->toChars()); - modules[i]->semantic3(); + // Generate DDoc output files. + if (global.params.doDocComments) { + for (unsigned i = 0; i < modules.dim; i++) { + gendocfile(modules[i]); } - if (global.errors) - fatal(); + } - Module::runDeferredSemantic3(); + // Generate the AST-describing JSON file. + if (global.params.doJsonGeneration) + emitJson(modules); - if (global.errors || global.warnings) - fatal(); + LLVM_D_FreeRuntime(); + llvm::llvm_shutdown(); - // Now that we analyzed all modules, write the module dependency file if - // the user requested it. - if (global.params.moduleDepsFile != NULL) - { - File deps(global.params.moduleDepsFile); - OutBuffer* ob = global.params.moduleDeps; - deps.setbuffer(static_cast(ob->data), ob->offset); - deps.write(); + if (global.errors) + fatal(); + + // Finally, produce the final executable/archive and run it, if we are + // supposed to. + int status = EXIT_SUCCESS; + if (!global.params.objfiles->dim) { + if (global.params.link) + error(Loc(), "no object files to link"); + else if (createStaticLib) + error(Loc(), "no object files"); + } else { + if (global.params.link) + status = linkObjToBinary(createSharedLib); + else if (createStaticLib) + status = createStaticLibrary(); + + if (global.params.run && status == EXIT_SUCCESS) { + status = runExecutable(); + + /// Delete .obj files and .exe file. + for (unsigned i = 0; i < modules.dim; i++) { + modules[i]->deleteObjFile(); + } + deleteExecutable(); } + } - // Generate one or more object/IR/bitcode files. - if (global.params.obj && !modules.empty()) - { - ldc::CodeGenerator cg(llvm::getGlobalContext(), singleObj); - - for (unsigned i = 0; i < modules.dim; i++) - { - Module * const m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "code %s\n", m->toChars()); - - cg.emit(m); - - if (global.errors) - fatal(); - } - } - - // Generate DDoc output files. - if (global.params.doDocComments) - { - for (unsigned i = 0; i < modules.dim; i++) - { - gendocfile(modules[i]); - } - } - - // Generate the AST-describing JSON file. - if (global.params.doJsonGeneration) - emitJson(modules); - - LLVM_D_FreeRuntime(); - llvm::llvm_shutdown(); - - if (global.errors) - fatal(); - - // Finally, produce the final executable/archive and run it, if we are - // supposed to. - int status = EXIT_SUCCESS; - if (!global.params.objfiles->dim) - { - if (global.params.link) - error(Loc(), "no object files to link"); - else if (createStaticLib) - error(Loc(), "no object files"); - } - else - { - if (global.params.link) - status = linkObjToBinary(createSharedLib); - else if (createStaticLib) - status = createStaticLibrary(); - - if (global.params.run && status == EXIT_SUCCESS) - { - status = runExecutable(); - - /// Delete .obj files and .exe file. - for (unsigned i = 0; i < modules.dim; i++) - { - modules[i]->deleteObjFile(); - } - deleteExecutable(); - } - } - - return status; + return status; } diff --git a/driver/response.cpp b/driver/response.cpp index 15000a291e..a9c84903b9 100644 --- a/driver/response.cpp +++ b/driver/response.cpp @@ -27,7 +27,8 @@ // returns true if the quote is unescaped bool applyBackslashRule(std::string &arg) { std::string::reverse_iterator it; - for (it = arg.rbegin(); it != arg.rend() && *it == '\\'; ++it) {} + for (it = arg.rbegin(); it != arg.rend() && *it == '\\'; ++it) { + } size_t numbs = std::distance(arg.rbegin(), it); bool escapedquote = numbs % 2; size_t numescaped = numbs / 2 + escapedquote; diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index 1a7f3537d0..716a756b40 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -29,296 +29,305 @@ #if LDC_LLVM_VER >= 307 #include "driver/cl_options.h" -static const char* getABI(const llvm::Triple &triple) -{ - llvm::StringRef ABIName(opts::mABI); - if (ABIName != "") - { - switch (triple.getArch()) - { - case llvm::Triple::arm: - case llvm::Triple::armeb: - if (ABIName.startswith("aapcs")) return "aapcs"; - if (ABIName.startswith("eabi")) return "apcs"; - break; - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - if (ABIName.startswith("o32")) return "o32"; - if (ABIName.startswith("n32")) return "n32"; - if (ABIName.startswith("n64")) return "n64"; - if (ABIName.startswith("eabi")) return "eabi"; - break; - case llvm::Triple::ppc64: - case llvm::Triple::ppc64le: - if (ABIName.startswith("elfv1")) return "elfv1"; - if (ABIName.startswith("elfv2")) return "elfv2"; - break; - default: - break; - } - warning(Loc(), "Unknown ABI %s - using default ABI instead", ABIName.str().c_str()); +static const char *getABI(const llvm::Triple &triple) { + llvm::StringRef ABIName(opts::mABI); + if (ABIName != "") { + switch (triple.getArch()) { + case llvm::Triple::arm: + case llvm::Triple::armeb: + if (ABIName.startswith("aapcs")) + return "aapcs"; + if (ABIName.startswith("eabi")) + return "apcs"; + break; + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + if (ABIName.startswith("o32")) + return "o32"; + if (ABIName.startswith("n32")) + return "n32"; + if (ABIName.startswith("n64")) + return "n64"; + if (ABIName.startswith("eabi")) + return "eabi"; + break; + case llvm::Triple::ppc64: + case llvm::Triple::ppc64le: + if (ABIName.startswith("elfv1")) + return "elfv1"; + if (ABIName.startswith("elfv2")) + return "elfv2"; + break; + default: + break; } + warning(Loc(), "Unknown ABI %s - using default ABI instead", + ABIName.str().c_str()); + } - switch (triple.getArch()) - { - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - return "n32"; - case llvm::Triple::ppc64: - return "elfv1"; - case llvm::Triple::ppc64le: - return "elfv2"; - default: - return ""; - } + switch (triple.getArch()) { + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + return "n32"; + case llvm::Triple::ppc64: + return "elfv1"; + case llvm::Triple::ppc64le: + return "elfv2"; + default: + return ""; + } } #endif -extern llvm::TargetMachine* gTargetMachine; +extern llvm::TargetMachine *gTargetMachine; -MipsABI::Type getMipsABI() -{ +MipsABI::Type getMipsABI() { #if LDC_LLVM_VER >= 307 - // eabi can only be set on the commandline - if (strncmp(opts::mABI.c_str(), "eabi", 4) == 0) - return MipsABI::EABI; - else - { + // eabi can only be set on the commandline + if (strncmp(opts::mABI.c_str(), "eabi", 4) == 0) + return MipsABI::EABI; + else { #if LDC_LLVM_VER >= 308 - const llvm::DataLayout dl = gTargetMachine->createDataLayout(); + const llvm::DataLayout dl = gTargetMachine->createDataLayout(); #else - const llvm::DataLayout& dl = *gTargetMachine->getDataLayout(); + const llvm::DataLayout &dl = *gTargetMachine->getDataLayout(); #endif - if (dl.getPointerSizeInBits() == 64) - return MipsABI::N64; - else if (dl.getLargestLegalIntTypeSize() == 64) - return MipsABI::N32; - else - return MipsABI::O32; - } + if (dl.getPointerSizeInBits() == 64) + return MipsABI::N64; + else if (dl.getLargestLegalIntTypeSize() == 64) + return MipsABI::N32; + else + return MipsABI::O32; + } #else - llvm::StringRef features = gTargetMachine->getTargetFeatureString(); - if (features.find("+o32") != std::string::npos) - return MipsABI::O32; - if (features.find("+n32") != std::string::npos) - return MipsABI::N32; - if (features.find("+n64") != std::string::npos) - return MipsABI::N32; - if (features.find("+eabi") != std::string::npos) - return MipsABI::EABI; - return MipsABI::Unknown; + llvm::StringRef features = gTargetMachine->getTargetFeatureString(); + if (features.find("+o32") != std::string::npos) + return MipsABI::O32; + if (features.find("+n32") != std::string::npos) + return MipsABI::N32; + if (features.find("+n64") != std::string::npos) + return MipsABI::N32; + if (features.find("+eabi") != std::string::npos) + return MipsABI::EABI; + return MipsABI::Unknown; #endif } -static std::string getX86TargetCPU(const llvm::Triple &triple) -{ - // Select the default CPU if none was given (or detection failed). +static std::string getX86TargetCPU(const llvm::Triple &triple) { + // Select the default CPU if none was given (or detection failed). - // Intel Macs are relatively recent, take advantage of that. - if (triple.isOSDarwin()) - return triple.isArch64Bit() ? "core2" : "yonah"; + // Intel Macs are relatively recent, take advantage of that. + if (triple.isOSDarwin()) + return triple.isArch64Bit() ? "core2" : "yonah"; - // Everything else goes to x86-64 in 64-bit mode. - if (triple.isArch64Bit()) - return "x86-64"; + // Everything else goes to x86-64 in 64-bit mode. + if (triple.isArch64Bit()) + return "x86-64"; - if (triple.getOSName().startswith("haiku")) - return "i586"; - if (triple.getOSName().startswith("openbsd")) - return "i486"; - if (triple.getOSName().startswith("bitrig")) - return "i686"; - if (triple.getOSName().startswith("freebsd")) - return "i486"; - if (triple.getOSName().startswith("netbsd")) - return "i486"; - // All x86 devices running Android have core2 as their common - // denominator. This makes a better choice than pentium4. - if (triple.getEnvironment() == llvm::Triple::Android) - return "core2"; + if (triple.getOSName().startswith("haiku")) + return "i586"; + if (triple.getOSName().startswith("openbsd")) + return "i486"; + if (triple.getOSName().startswith("bitrig")) + return "i686"; + if (triple.getOSName().startswith("freebsd")) + return "i486"; + if (triple.getOSName().startswith("netbsd")) + return "i486"; + // All x86 devices running Android have core2 as their common + // denominator. This makes a better choice than pentium4. + if (triple.getEnvironment() == llvm::Triple::Android) + return "core2"; - // Fallback to p4. - return "pentium4"; + // Fallback to p4. + return "pentium4"; } -static std::string getARMTargetCPU(const llvm::Triple &triple) -{ - const char *result = llvm::StringSwitch(triple.getArchName()) - .Cases("armv2", "armv2a","arm2") - .Case("armv3", "arm6") - .Case("armv3m", "arm7m") - .Case("armv4", "strongarm") - .Case("armv4t", "arm7tdmi") - .Cases("armv5", "armv5t", "arm10tdmi") - .Cases("armv5e", "armv5te", "arm1026ejs") - .Case("armv5tej", "arm926ej-s") - .Cases("armv6", "armv6k", "arm1136jf-s") - .Case("armv6j", "arm1136j-s") - .Cases("armv6z", "armv6zk", "arm1176jzf-s") - .Case("armv6t2", "arm1156t2-s") - .Cases("armv6m", "armv6-m", "cortex-m0") - .Cases("armv7", "armv7a", "armv7-a", "cortex-a8") - .Cases("armv7l", "armv7-l", "cortex-a8") - .Cases("armv7f", "armv7-f", "cortex-a9-mp") - .Cases("armv7s", "armv7-s", "swift") - .Cases("armv7r", "armv7-r", "cortex-r4") - .Cases("armv7m", "armv7-m", "cortex-m3") - .Cases("armv7em", "armv7e-m", "cortex-m4") - .Cases("armv8", "armv8a", "armv8-a", "cortex-a53") - .Case("ep9312", "ep9312") - .Case("iwmmxt", "iwmmxt") - .Case("xscale", "xscale") - // If all else failed, return the most base CPU with thumb interworking - // supported by LLVM. - .Default(0); +static std::string getARMTargetCPU(const llvm::Triple &triple) { + const char *result = llvm::StringSwitch(triple.getArchName()) + .Cases("armv2", "armv2a", "arm2") + .Case("armv3", "arm6") + .Case("armv3m", "arm7m") + .Case("armv4", "strongarm") + .Case("armv4t", "arm7tdmi") + .Cases("armv5", "armv5t", "arm10tdmi") + .Cases("armv5e", "armv5te", "arm1026ejs") + .Case("armv5tej", "arm926ej-s") + .Cases("armv6", "armv6k", "arm1136jf-s") + .Case("armv6j", "arm1136j-s") + .Cases("armv6z", "armv6zk", "arm1176jzf-s") + .Case("armv6t2", "arm1156t2-s") + .Cases("armv6m", "armv6-m", "cortex-m0") + .Cases("armv7", "armv7a", "armv7-a", "cortex-a8") + .Cases("armv7l", "armv7-l", "cortex-a8") + .Cases("armv7f", "armv7-f", "cortex-a9-mp") + .Cases("armv7s", "armv7-s", "swift") + .Cases("armv7r", "armv7-r", "cortex-r4") + .Cases("armv7m", "armv7-m", "cortex-m3") + .Cases("armv7em", "armv7e-m", "cortex-m4") + .Cases("armv8", "armv8a", "armv8-a", "cortex-a53") + .Case("ep9312", "ep9312") + .Case("iwmmxt", "iwmmxt") + .Case("xscale", "xscale") + // If all else failed, return the most base CPU with + // thumb interworking + // supported by LLVM. + .Default(0); - if (result) - return result; + if (result) + return result; - return (triple.getEnvironment() == llvm::Triple::GNUEABIHF) ? - "arm1176jzf-s" : "arm7tdmi"; + return (triple.getEnvironment() == llvm::Triple::GNUEABIHF) ? "arm1176jzf-s" + : "arm7tdmi"; } /// Returns the LLVM name of the target CPU to use given the provided /// -mcpu argument and target triple. static std::string getTargetCPU(const std::string &cpu, - const llvm::Triple &triple) -{ - if (!cpu.empty()) - { - if (cpu != "native") - return cpu; + const llvm::Triple &triple) { + if (!cpu.empty()) { + if (cpu != "native") + return cpu; - // FIXME: Reject attempts to use -mcpu=native unless the target matches - // the host. - std::string hostCPU = llvm::sys::getHostCPUName(); - if (!hostCPU.empty() && hostCPU != "generic") - return hostCPU; - } + // FIXME: Reject attempts to use -mcpu=native unless the target matches + // the host. + std::string hostCPU = llvm::sys::getHostCPUName(); + if (!hostCPU.empty() && hostCPU != "generic") + return hostCPU; + } - switch (triple.getArch()) - { - default: - // We don't know about the specifics of this platform, just return the - // empty string and let LLVM decide. - return cpu; - case llvm::Triple::x86: - case llvm::Triple::x86_64: - return getX86TargetCPU(triple); - case llvm::Triple::arm: - return getARMTargetCPU(triple); - } + switch (triple.getArch()) { + default: + // We don't know about the specifics of this platform, just return the + // empty string and let LLVM decide. + return cpu; + case llvm::Triple::x86: + case llvm::Triple::x86_64: + return getX86TargetCPU(triple); + case llvm::Triple::arm: + return getARMTargetCPU(triple); + } } -static const char *getLLVMArchSuffixForARM(llvm::StringRef CPU) -{ - return llvm::StringSwitch(CPU) - .Case("strongarm", "v4") - .Cases("arm7tdmi", "arm7tdmi-s", "arm710t", "v4t") - .Cases("arm720t", "arm9", "arm9tdmi", "v4t") - .Cases("arm920", "arm920t", "arm922t", "v4t") - .Cases("arm940t", "ep9312","v4t") - .Cases("arm10tdmi", "arm1020t", "v5") - .Cases("arm9e", "arm926ej-s", "arm946e-s", "v5e") - .Cases("arm966e-s", "arm968e-s", "arm10e", "v5e") - .Cases("arm1020e", "arm1022e", "xscale", "iwmmxt", "v5e") - .Cases("arm1136j-s", "arm1136jf-s", "arm1176jz-s", "v6") - .Cases("arm1176jzf-s", "mpcorenovfp", "mpcore", "v6") - .Cases("arm1156t2-s", "arm1156t2f-s", "v6t2") - .Cases("cortex-a5", "cortex-a7", "cortex-a8", "v7") - .Cases("cortex-a9", "cortex-a12", "cortex-a15", "v7") - .Cases("cortex-r4", "cortex-r5", "v7r") - .Case("cortex-m0", "v6m") - .Case("cortex-m3", "v7m") - .Case("cortex-m4", "v7em") - .Case("cortex-a9-mp", "v7f") - .Case("swift", "v7s") - .Case("cortex-a53", "v8") - .Case("krait", "v7") - .Default(""); +static const char *getLLVMArchSuffixForARM(llvm::StringRef CPU) { + return llvm::StringSwitch(CPU) + .Case("strongarm", "v4") + .Cases("arm7tdmi", "arm7tdmi-s", "arm710t", "v4t") + .Cases("arm720t", "arm9", "arm9tdmi", "v4t") + .Cases("arm920", "arm920t", "arm922t", "v4t") + .Cases("arm940t", "ep9312", "v4t") + .Cases("arm10tdmi", "arm1020t", "v5") + .Cases("arm9e", "arm926ej-s", "arm946e-s", "v5e") + .Cases("arm966e-s", "arm968e-s", "arm10e", "v5e") + .Cases("arm1020e", "arm1022e", "xscale", "iwmmxt", "v5e") + .Cases("arm1136j-s", "arm1136jf-s", "arm1176jz-s", "v6") + .Cases("arm1176jzf-s", "mpcorenovfp", "mpcore", "v6") + .Cases("arm1156t2-s", "arm1156t2f-s", "v6t2") + .Cases("cortex-a5", "cortex-a7", "cortex-a8", "v7") + .Cases("cortex-a9", "cortex-a12", "cortex-a15", "v7") + .Cases("cortex-r4", "cortex-r5", "v7r") + .Case("cortex-m0", "v6m") + .Case("cortex-m3", "v7m") + .Case("cortex-m4", "v7em") + .Case("cortex-a9-mp", "v7f") + .Case("swift", "v7s") + .Case("cortex-a53", "v8") + .Case("krait", "v7") + .Default(""); } static FloatABI::Type getARMFloatABI(const llvm::Triple &triple, - const char* llvmArchSuffix) -{ - switch (triple.getOS()) { - case llvm::Triple::Darwin: - case llvm::Triple::MacOSX: - case llvm::Triple::IOS: { - // Darwin defaults to "softfp" for v6 and v7. - if (llvm::StringRef(llvmArchSuffix).startswith("v6") || - llvm::StringRef(llvmArchSuffix).startswith("v7")) - return FloatABI::SoftFP; - return FloatABI::Soft; + const char *llvmArchSuffix) { + switch (triple.getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: { + // Darwin defaults to "softfp" for v6 and v7. + if (llvm::StringRef(llvmArchSuffix).startswith("v6") || + llvm::StringRef(llvmArchSuffix).startswith("v7")) + return FloatABI::SoftFP; + return FloatABI::Soft; + } + + case llvm::Triple::FreeBSD: + // FreeBSD defaults to soft float + return FloatABI::Soft; + + default: + switch (triple.getEnvironment()) { + case llvm::Triple::GNUEABIHF: + return FloatABI::Hard; + case llvm::Triple::GNUEABI: + return FloatABI::SoftFP; + case llvm::Triple::EABI: + // EABI is always AAPCS, and if it was not marked 'hard', it's softfp + return FloatABI::SoftFP; + case llvm::Triple::Android: { + if (llvm::StringRef(llvmArchSuffix).startswith("v7")) + return FloatABI::SoftFP; + return FloatABI::Soft; } - - case llvm::Triple::FreeBSD: - // FreeBSD defaults to soft float - return FloatABI::Soft; - default: - switch(triple.getEnvironment()) { - case llvm::Triple::GNUEABIHF: - return FloatABI::Hard; - case llvm::Triple::GNUEABI: - return FloatABI::SoftFP; - case llvm::Triple::EABI: - // EABI is always AAPCS, and if it was not marked 'hard', it's softfp - return FloatABI::SoftFP; - case llvm::Triple::Android: { - if (llvm::StringRef(llvmArchSuffix).startswith("v7")) - return FloatABI::SoftFP; - return FloatABI::Soft; - } - default: - // Assume "soft". - // TODO: Warn the user we are guessing. - return FloatABI::Soft; - } + // Assume "soft". + // TODO: Warn the user we are guessing. + return FloatABI::Soft; } + } } #if LDC_LLVM_VER < 307 /// Sanitizes the MIPS ABI in the feature string. -static void addMipsABI(const llvm::Triple &triple, std::vector &attrs) -{ - enum ABI { O32 = 1<<0, N32 = 1<<1, N64 = 1<<2, EABI = 1<<3 }; - const bool is64Bit = triple.getArch() == llvm::Triple::mips64 || - triple.getArch() == llvm::Triple::mips64el; - const uint32_t defaultABI = is64Bit ? N64 : O32; - uint32_t bits = defaultABI; - auto I = attrs.begin(); - while (I != attrs.end()) - { - std::string str = *I; - bool enabled = str[0] == '+'; - std::string flag = (str[0] == '+' || str[0] == '-') ? str.substr(1) : str; - uint32_t newBit = 0; - if (flag == "o32") newBit = O32; - if (flag == "n32") newBit = N32; - if (flag == "n64") newBit = N64; - if (flag == "eabi") newBit = EABI; - if (newBit) - { - I = attrs.erase(I); - if (enabled) bits |= newBit; - else bits &= ~newBit; - } - else - ++I; - } - switch (bits) - { - case O32: attrs.push_back("+o32"); break; - case N32: attrs.push_back("+n32"); break; - case N64: attrs.push_back("+n64"); break; - case EABI: attrs.push_back("+eabi"); break; - default: error(Loc(), "Only one ABI argument is supported"); fatal(); - } - if (bits != defaultABI) - attrs.push_back(is64Bit ? "-n64" : "-o32"); +static void addMipsABI(const llvm::Triple &triple, + std::vector &attrs) { + enum ABI { O32 = 1 << 0, N32 = 1 << 1, N64 = 1 << 2, EABI = 1 << 3 }; + const bool is64Bit = triple.getArch() == llvm::Triple::mips64 || + triple.getArch() == llvm::Triple::mips64el; + const uint32_t defaultABI = is64Bit ? N64 : O32; + uint32_t bits = defaultABI; + auto I = attrs.begin(); + while (I != attrs.end()) { + std::string str = *I; + bool enabled = str[0] == '+'; + std::string flag = (str[0] == '+' || str[0] == '-') ? str.substr(1) : str; + uint32_t newBit = 0; + if (flag == "o32") + newBit = O32; + if (flag == "n32") + newBit = N32; + if (flag == "n64") + newBit = N64; + if (flag == "eabi") + newBit = EABI; + if (newBit) { + I = attrs.erase(I); + if (enabled) + bits |= newBit; + else + bits &= ~newBit; + } else + ++I; + } + switch (bits) { + case O32: + attrs.push_back("+o32"); + break; + case N32: + attrs.push_back("+n32"); + break; + case N64: + attrs.push_back("+n64"); + break; + case EABI: + attrs.push_back("+eabi"); + break; + default: + error(Loc(), "Only one ABI argument is supported"); + fatal(); + } + if (bits != defaultABI) + attrs.push_back(is64Bit ? "-n64" : "-o32"); } #endif @@ -331,218 +340,187 @@ static void addMipsABI(const llvm::Triple &triple, std::vector &att /// llvm::TargetRegistry::lookupTarget. Once support for LLVM 3.1 is dropped, /// the registry method can be used instead. const llvm::Target *lookupTarget(const std::string &arch, llvm::Triple &triple, - std::string &errorMsg) -{ - // Allocate target machine. First, check whether the user has explicitly - // specified an architecture to compile for. If so we have to look it up by - // name, because it might be a backend that has no mapping to a target triple. - const llvm::Target *target = 0; - if (!arch.empty()) - { + std::string &errorMsg) { + // Allocate target machine. First, check whether the user has explicitly + // specified an architecture to compile for. If so we have to look it up by + // name, because it might be a backend that has no mapping to a target triple. + const llvm::Target *target = 0; + if (!arch.empty()) { #if LDC_LLVM_VER >= 307 - for (const llvm::Target &T : llvm::TargetRegistry::targets()) - { + for (const llvm::Target &T : llvm::TargetRegistry::targets()) { #else - for (auto it = llvm::TargetRegistry::begin(), ie = llvm::TargetRegistry::end(); it != ie; ++it) - { - const llvm::Target& T = *it; + for (auto it = llvm::TargetRegistry::begin(), + ie = llvm::TargetRegistry::end(); + it != ie; ++it) { + const llvm::Target &T = *it; #endif - if (arch == T.getName()) - { - target = &T; - break; - } - } - - if (!target) - { - errorMsg = "invalid target architecture '" + arch + "', see " - "-version for a list of supported targets."; - return 0; - } - - // Adjust the triple to match (if known), otherwise stick with the - // given triple. - llvm::Triple::ArchType Type = llvm::Triple::getArchTypeForLLVMName(arch); - if (Type != llvm::Triple::UnknownArch) - triple.setArch(Type); - } - else - { - std::string tempError; - target = llvm::TargetRegistry::lookupTarget(triple.getTriple(), tempError); - if (!target) - { - errorMsg = "unable to get target for '" + triple.getTriple() + - "', see -version and -mtriple."; - } + if (arch == T.getName()) { + target = &T; + break; + } } - return target; + if (!target) { + errorMsg = "invalid target architecture '" + arch + + "', see " + "-version for a list of supported targets."; + return 0; + } + + // Adjust the triple to match (if known), otherwise stick with the + // given triple. + llvm::Triple::ArchType Type = llvm::Triple::getArchTypeForLLVMName(arch); + if (Type != llvm::Triple::UnknownArch) + triple.setArch(Type); + } else { + std::string tempError; + target = llvm::TargetRegistry::lookupTarget(triple.getTriple(), tempError); + if (!target) { + errorMsg = "unable to get target for '" + triple.getTriple() + + "', see -version and -mtriple."; + } + } + + return target; } -llvm::TargetMachine* createTargetMachine( - std::string targetTriple, - std::string arch, - std::string cpu, - std::vector attrs, - ExplicitBitness::Type bitness, - FloatABI::Type floatABI, - llvm::Reloc::Model relocModel, - llvm::CodeModel::Model codeModel, - llvm::CodeGenOpt::Level codeGenOptLevel, - bool noFramePointerElim, - bool noLinkerStripDead) -{ - // Determine target triple. If the user didn't explicitly specify one, use - // the one set at LLVM configure time. - llvm::Triple triple; - if (targetTriple.empty()) - { - triple = llvm::Triple(llvm::sys::getDefaultTargetTriple()); +llvm::TargetMachine *createTargetMachine( + std::string targetTriple, std::string arch, std::string cpu, + std::vector attrs, ExplicitBitness::Type bitness, + FloatABI::Type floatABI, llvm::Reloc::Model relocModel, + llvm::CodeModel::Model codeModel, llvm::CodeGenOpt::Level codeGenOptLevel, + bool noFramePointerElim, bool noLinkerStripDead) { + // Determine target triple. If the user didn't explicitly specify one, use + // the one set at LLVM configure time. + llvm::Triple triple; + if (targetTriple.empty()) { + triple = llvm::Triple(llvm::sys::getDefaultTargetTriple()); - // Handle -m32/-m64. - if (sizeof(void*) == 4 && bitness == ExplicitBitness::M64) - { - triple = triple.get64BitArchVariant(); - } - else if (sizeof(void*) == 8 && bitness == ExplicitBitness::M32) - { - triple = triple.get32BitArchVariant(); - } - } - else - { - triple = llvm::Triple(llvm::Triple::normalize(targetTriple)); + // Handle -m32/-m64. + if (sizeof(void *) == 4 && bitness == ExplicitBitness::M64) { + triple = triple.get64BitArchVariant(); + } else if (sizeof(void *) == 8 && bitness == ExplicitBitness::M32) { + triple = triple.get32BitArchVariant(); } + } else { + triple = llvm::Triple(llvm::Triple::normalize(targetTriple)); + } - // Look up the LLVM backend to use. This also updates triple with the - // user-specified arch, if any. - std::string errMsg; - const llvm::Target *target = lookupTarget(arch, triple, errMsg); - if (target == 0) - { - error(Loc(), "%s", errMsg.c_str()); - fatal(); - } + // Look up the LLVM backend to use. This also updates triple with the + // user-specified arch, if any. + std::string errMsg; + const llvm::Target *target = lookupTarget(arch, triple, errMsg); + if (target == 0) { + error(Loc(), "%s", errMsg.c_str()); + fatal(); + } - // Package up features to be passed to target/subtarget. - llvm::SubtargetFeatures features; - features.getDefaultSubtargetFeatures(triple); - if (cpu == "native") - { - llvm::StringMap hostFeatures; - if (llvm::sys::getHostCPUFeatures(hostFeatures)) - { - for (const auto& hf : hostFeatures) - features.AddFeature(std::string(hf.second ? "+" : "-").append(hf.first())); - } + // Package up features to be passed to target/subtarget. + llvm::SubtargetFeatures features; + features.getDefaultSubtargetFeatures(triple); + if (cpu == "native") { + llvm::StringMap hostFeatures; + if (llvm::sys::getHostCPUFeatures(hostFeatures)) { + for (const auto &hf : hostFeatures) + features.AddFeature( + std::string(hf.second ? "+" : "-").append(hf.first())); } + } #if LDC_LLVM_VER < 307 - if (triple.getArch() == llvm::Triple::mips || - triple.getArch() == llvm::Triple::mipsel || - triple.getArch() == llvm::Triple::mips64 || - triple.getArch() == llvm::Triple::mips64el) - addMipsABI(triple, attrs); + if (triple.getArch() == llvm::Triple::mips || + triple.getArch() == llvm::Triple::mipsel || + triple.getArch() == llvm::Triple::mips64 || + triple.getArch() == llvm::Triple::mips64el) + addMipsABI(triple, attrs); #endif + for (unsigned i = 0; i < attrs.size(); ++i) + features.AddFeature(attrs[i]); + + // With an empty CPU string, LLVM will default to the host CPU, which is + // usually not what we want (expected behavior from other compilers is + // to default to "generic"). + cpu = getTargetCPU(cpu, triple); + + // cmpxchg16b is not available on old 64bit CPUs. Enable code generation + // if the user did not make an explicit choice. + if (cpu == "x86-64") { + const char *cx16_plus = "+cx16"; + const char *cx16_minus = "-cx16"; + bool cx16 = false; for (unsigned i = 0; i < attrs.size(); ++i) - features.AddFeature(attrs[i]); + if (attrs[i] == cx16_plus || attrs[i] == cx16_minus) + cx16 = true; + if (!cx16) + features.AddFeature(cx16_plus); + } - // With an empty CPU string, LLVM will default to the host CPU, which is - // usually not what we want (expected behavior from other compilers is - // to default to "generic"). - cpu = getTargetCPU(cpu, triple); + if (Logger::enabled()) { + Logger::println("Targeting '%s' (CPU '%s' with features '%s')", + triple.str().c_str(), cpu.c_str(), + features.getString().c_str()); + } - // cmpxchg16b is not available on old 64bit CPUs. Enable code generation - // if the user did not make an explicit choice. - if (cpu == "x86-64") - { - const char* cx16_plus = "+cx16"; - const char* cx16_minus = "-cx16"; - bool cx16 = false; - for (unsigned i = 0; i < attrs.size(); ++i) - if (attrs[i] == cx16_plus || attrs[i] == cx16_minus) cx16 = true; - if (!cx16) - features.AddFeature(cx16_plus); + if (triple.isMacOSX() && relocModel == llvm::Reloc::Default) { + // OS X defaults to PIC (and as of 10.7.5/LLVM 3.1-3.3, TLS use leads + // to crashes for non-PIC code). LLVM doesn't handle this. + relocModel = llvm::Reloc::PIC_; + } + + if (floatABI == FloatABI::Default) { + switch (triple.getArch()) { + default: // X86, ... + floatABI = FloatABI::Hard; + break; + case llvm::Triple::arm: + case llvm::Triple::thumb: + floatABI = getARMFloatABI(triple, getLLVMArchSuffixForARM(cpu)); + break; } + } - if (Logger::enabled()) - { - Logger::println("Targeting '%s' (CPU '%s' with features '%s')", - triple.str().c_str(), cpu.c_str(), features.getString().c_str()); - } - - if (triple.isMacOSX() && relocModel == llvm::Reloc::Default) - { - // OS X defaults to PIC (and as of 10.7.5/LLVM 3.1-3.3, TLS use leads - // to crashes for non-PIC code). LLVM doesn't handle this. - relocModel = llvm::Reloc::PIC_; - } - - if (floatABI == FloatABI::Default) - { - switch (triple.getArch()) - { - default: // X86, ... - floatABI = FloatABI::Hard; - break; - case llvm::Triple::arm: - case llvm::Triple::thumb: - floatABI = getARMFloatABI(triple, getLLVMArchSuffixForARM(cpu)); - break; - } - } - - llvm::TargetOptions targetOptions; + llvm::TargetOptions targetOptions; #if LDC_LLVM_VER < 307 - targetOptions.NoFramePointerElim = noFramePointerElim; + targetOptions.NoFramePointerElim = noFramePointerElim; #endif #if LDC_LLVM_VER >= 307 - targetOptions.MCOptions.ABIName = getABI(triple); + targetOptions.MCOptions.ABIName = getABI(triple); #endif - switch (floatABI) - { - default: llvm_unreachable("Floating point ABI type unknown."); - case FloatABI::Soft: + switch (floatABI) { + default: + llvm_unreachable("Floating point ABI type unknown."); + case FloatABI::Soft: #if LDC_LLVM_VER < 307 - targetOptions.UseSoftFloat = true; + targetOptions.UseSoftFloat = true; #endif - targetOptions.FloatABIType = llvm::FloatABI::Soft; - break; - case FloatABI::SoftFP: + targetOptions.FloatABIType = llvm::FloatABI::Soft; + break; + case FloatABI::SoftFP: #if LDC_LLVM_VER < 307 - targetOptions.UseSoftFloat = false; + targetOptions.UseSoftFloat = false; #endif - targetOptions.FloatABIType = llvm::FloatABI::Soft; - break; - case FloatABI::Hard: + targetOptions.FloatABIType = llvm::FloatABI::Soft; + break; + case FloatABI::Hard: #if LDC_LLVM_VER < 307 - targetOptions.UseSoftFloat = false; + targetOptions.UseSoftFloat = false; #endif - targetOptions.FloatABIType = llvm::FloatABI::Hard; - break; - } + targetOptions.FloatABIType = llvm::FloatABI::Hard; + break; + } - // Right now, we only support linker-level dead code elimination on Linux - // using the GNU toolchain (based on ld's --gc-sections flag). The Apple ld - // on OS X supports a similar flag (-dead_strip) that doesn't require - // emitting the symbols into different sections. The MinGW ld doesn't seem - // to support --gc-sections at all, and FreeBSD needs more investigation. - if (!noLinkerStripDead && - (triple.getOS() == llvm::Triple::Linux || triple.getOS() == llvm::Triple::Win32)) - { - targetOptions.FunctionSections = true; - targetOptions.DataSections = true; - } + // Right now, we only support linker-level dead code elimination on Linux + // using the GNU toolchain (based on ld's --gc-sections flag). The Apple ld + // on OS X supports a similar flag (-dead_strip) that doesn't require + // emitting the symbols into different sections. The MinGW ld doesn't seem + // to support --gc-sections at all, and FreeBSD needs more investigation. + if (!noLinkerStripDead && (triple.getOS() == llvm::Triple::Linux || + triple.getOS() == llvm::Triple::Win32)) { + targetOptions.FunctionSections = true; + targetOptions.DataSections = true; + } - return target->createTargetMachine( - triple.str(), - cpu, - features.getString(), - targetOptions, - relocModel, - codeModel, - codeGenOptLevel - ); + return target->createTargetMachine(triple.str(), cpu, features.getString(), + targetOptions, relocModel, codeModel, + codeGenOptLevel); } diff --git a/driver/targetmachine.h b/driver/targetmachine.h index d33deb1116..24eed8aad2 100644 --- a/driver/targetmachine.h +++ b/driver/targetmachine.h @@ -20,33 +20,20 @@ #include namespace ExplicitBitness { - enum Type { - None, - M32, - M64 - }; +enum Type { None, M32, M64 }; } namespace FloatABI { - enum Type { - Default, - Soft, - SoftFP, - Hard - }; +enum Type { Default, Soft, SoftFP, Hard }; } namespace MipsABI { - enum Type { - Unknown, - O32, - N32, - N64, - EABI - }; +enum Type { Unknown, O32, N32, N64, EABI }; } -namespace llvm { class TargetMachine; } +namespace llvm { +class TargetMachine; +} /** * Creates an LLVM TargetMachine suitable for the given (usually command-line) @@ -54,19 +41,12 @@ namespace llvm { class TargetMachine; } * * Does not depend on any global state. */ -llvm::TargetMachine* createTargetMachine( - std::string targetTriple, - std::string arch, - std::string cpu, - std::vector attrs, - ExplicitBitness::Type bitness, - FloatABI::Type floatABI, - llvm::Reloc::Model relocModel, - llvm::CodeModel::Model codeModel, - llvm::CodeGenOpt::Level codeGenOptLevel, - bool noFramePointerElim, - bool noLinkerStripDead - ); +llvm::TargetMachine *createTargetMachine( + std::string targetTriple, std::string arch, std::string cpu, + std::vector attrs, ExplicitBitness::Type bitness, + FloatABI::Type floatABI, llvm::Reloc::Model relocModel, + llvm::CodeModel::Model codeModel, llvm::CodeGenOpt::Level codeGenOptLevel, + bool noFramePointerElim, bool noLinkerStripDead); /** * Returns the Mips ABI which is used for code generation. diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 61ff5b21e5..f6192a70ad 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -41,459 +41,428 @@ #include static llvm::cl::opt -NoIntegratedAssembler("no-integrated-as", llvm::cl::Hidden, - llvm::cl::desc("Disable integrated assembler")); + NoIntegratedAssembler("no-integrated-as", llvm::cl::Hidden, + llvm::cl::desc("Disable integrated assembler")); // based on llc code, University of Illinois Open Source License -static void codegenModule(llvm::TargetMachine &Target, llvm::Module& m, - llvm::raw_fd_ostream& out, llvm::TargetMachine::CodeGenFileType fileType) -{ - using namespace llvm; +static void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, + llvm::raw_fd_ostream &out, + llvm::TargetMachine::CodeGenFileType fileType) { + using namespace llvm; - // Create a PassManager to hold and optimize the collection of passes we are - // about to build. +// Create a PassManager to hold and optimize the collection of passes we are +// about to build. #if LDC_LLVM_VER >= 307 - legacy:: + legacy:: #endif - PassManager Passes; + PassManager Passes; #if LDC_LLVM_VER >= 307 - // The DataLayout is already set at the module (in module.cpp, - // method Module::genLLVMModule()) - // FIXME: Introduce new command line switch default-data-layout to - // override the module data layout +// The DataLayout is already set at the module (in module.cpp, +// method Module::genLLVMModule()) +// FIXME: Introduce new command line switch default-data-layout to +// override the module data layout #elif LDC_LLVM_VER == 306 - Passes.add(new DataLayoutPass()); + Passes.add(new DataLayoutPass()); #else - if (const DataLayout *DL = Target.getDataLayout()) - Passes.add(new DataLayoutPass(*DL)); - else - Passes.add(new DataLayoutPass(&m)); + if (const DataLayout *DL = Target.getDataLayout()) + Passes.add(new DataLayoutPass(*DL)); + else + Passes.add(new DataLayoutPass(&m)); #endif #if LDC_LLVM_VER >= 307 - // Add internal analysis passes from the target machine. - Passes.add(createTargetTransformInfoWrapperPass(Target.getTargetIRAnalysis())); + // Add internal analysis passes from the target machine. + Passes.add( + createTargetTransformInfoWrapperPass(Target.getTargetIRAnalysis())); #else - Target.addAnalysisPasses(Passes); + Target.addAnalysisPasses(Passes); #endif #if LDC_LLVM_VER < 307 - llvm::formatted_raw_ostream fout(out); + llvm::formatted_raw_ostream fout(out); #endif - if (Target.addPassesToEmitFile(Passes, + if (Target.addPassesToEmitFile(Passes, #if LDC_LLVM_VER >= 307 - out, + out, #else - fout, + fout, #endif - fileType, codeGenOptLevel())) - llvm_unreachable("no support for asm output"); + fileType, codeGenOptLevel())) + llvm_unreachable("no support for asm output"); - Passes.run(m); + Passes.run(m); } -static void assemble(const std::string &asmpath, const std::string &objpath) -{ - std::vector args; - args.push_back("-O3"); - args.push_back("-c"); - args.push_back("-xassembler"); - args.push_back(asmpath); - args.push_back("-o"); - args.push_back(objpath); +static void assemble(const std::string &asmpath, const std::string &objpath) { + std::vector args; + args.push_back("-O3"); + args.push_back("-c"); + args.push_back("-xassembler"); + args.push_back(asmpath); + args.push_back("-o"); + args.push_back(objpath); - // Only specify -m32/-m64 for architectures where the two variants actually - // exist (as e.g. the GCC ARM toolchain doesn't recognize the switches). - // MIPS does not have -m32/-m64 but requires -mabi=. - if (global.params.targetTriple.get64BitArchVariant().getArch() != - llvm::Triple::UnknownArch && - global.params.targetTriple.get32BitArchVariant().getArch() != - llvm::Triple::UnknownArch) { - if (global.params.targetTriple.get64BitArchVariant().getArch() == + // Only specify -m32/-m64 for architectures where the two variants actually + // exist (as e.g. the GCC ARM toolchain doesn't recognize the switches). + // MIPS does not have -m32/-m64 but requires -mabi=. + if (global.params.targetTriple.get64BitArchVariant().getArch() != + llvm::Triple::UnknownArch && + global.params.targetTriple.get32BitArchVariant().getArch() != + llvm::Triple::UnknownArch) { + if (global.params.targetTriple.get64BitArchVariant().getArch() == llvm::Triple::mips64 || - global.params.targetTriple.get64BitArchVariant().getArch() == + global.params.targetTriple.get64BitArchVariant().getArch() == llvm::Triple::mips64el) { - switch (getMipsABI()) - { - case MipsABI::EABI: - args.push_back("-mabi=eabi"); - args.push_back("-march=mips32r2"); - break; - case MipsABI::O32: - args.push_back("-mabi=32"); - args.push_back("-march=mips32r2"); - break; - case MipsABI::N32: - args.push_back("-mabi=n32"); - args.push_back("-march=mips64r2"); - break; - case MipsABI::N64: - args.push_back("-mabi=64"); - args.push_back("-march=mips64r2"); - break; - case MipsABI::Unknown: - break; - } - } - else { - if (global.params.is64bit) - args.push_back("-m64"); - else - args.push_back("-m32"); - } + switch (getMipsABI()) { + case MipsABI::EABI: + args.push_back("-mabi=eabi"); + args.push_back("-march=mips32r2"); + break; + case MipsABI::O32: + args.push_back("-mabi=32"); + args.push_back("-march=mips32r2"); + break; + case MipsABI::N32: + args.push_back("-mabi=n32"); + args.push_back("-march=mips64r2"); + break; + case MipsABI::N64: + args.push_back("-mabi=64"); + args.push_back("-march=mips64r2"); + break; + case MipsABI::Unknown: + break; + } + } else { + if (global.params.is64bit) + args.push_back("-m64"); + else + args.push_back("-m32"); } + } - // Run the compiler to assembly the program. - std::string gcc(getGcc()); - int R = executeToolAndWait(gcc, args, global.params.verbose); - if (R) - { - error(Loc(), "Error while invoking external assembler."); - fatal(); - } + // Run the compiler to assembly the program. + std::string gcc(getGcc()); + int R = executeToolAndWait(gcc, args, global.params.verbose); + if (R) { + error(Loc(), "Error while invoking external assembler."); + fatal(); + } } ////////////////////////////////////////////////////////////////////////////////////////// -namespace -{ - using namespace llvm; - static void printDebugLoc(const DebugLoc& debugLoc, formatted_raw_ostream& os) - { - os << debugLoc.getLine() << ":" << debugLoc.getCol(); +namespace { +using namespace llvm; +static void printDebugLoc(const DebugLoc &debugLoc, formatted_raw_ostream &os) { + os << debugLoc.getLine() << ":" << debugLoc.getCol(); #if LDC_LLVM_VER >= 307 - if (DILocation *IDL = debugLoc.getInlinedAt()) - { - os << "@"; - printDebugLoc(IDL, os); - } + if (DILocation *IDL = debugLoc.getInlinedAt()) { + os << "@"; + printDebugLoc(IDL, os); + } #else - if (MDNode *N = debugLoc.getInlinedAt(getGlobalContext())) - { - DebugLoc IDL = DebugLoc::getFromDILocation(N); - if (!IDL.isUnknown()) - { - os << "@"; - printDebugLoc(IDL, os); - } - } + if (MDNode *N = debugLoc.getInlinedAt(getGlobalContext())) { + DebugLoc IDL = DebugLoc::getFromDILocation(N); + if (!IDL.isUnknown()) { + os << "@"; + printDebugLoc(IDL, os); + } + } +#endif +} + +class AssemblyAnnotator : public AssemblyAnnotationWriter { +// Find the MDNode which corresponds to the DISubprogram data that described F. +#if LDC_LLVM_VER >= 307 + static DISubprogram *FindSubprogram(const Function *F, + DebugInfoFinder &Finder) +#else + static MDNode *FindSubprogram(const Function *F, DebugInfoFinder &Finder) +#endif + { +#if LDC_LLVM_VER >= 307 + for (DISubprogram *Subprogram : Finder.subprograms()) + if (Subprogram->describes(F)) + return Subprogram; + return nullptr; +#else + for (DISubprogram Subprogram : Finder.subprograms()) + if (Subprogram.describes(F)) + return Subprogram; + return nullptr; +#endif + } + + static llvm::StringRef GetDisplayName(const Function *F) { + llvm::DebugInfoFinder Finder; + Finder.processModule(*F->getParent()); +#if LDC_LLVM_VER >= 307 + if (DISubprogram *N = FindSubprogram(F, Finder)) +#else + if (MDNode *N = FindSubprogram(F, Finder)) +#endif + { +#if LDC_LLVM_VER >= 307 + return N->getDisplayName(); +#else + llvm::DISubprogram sub(N); + return sub.getDisplayName(); #endif } + return ""; + } - class AssemblyAnnotator : public AssemblyAnnotationWriter +public: + void emitFunctionAnnot(const Function *F, + formatted_raw_ostream &os) LLVM_OVERRIDE { + os << "; [#uses = " << F->getNumUses() << ']'; + + // show demangled name + llvm::StringRef funcName = GetDisplayName(F); + if (!funcName.empty()) + os << " [display name = " << funcName << ']'; + + os << '\n'; + } + + void printInfoComment(const Value &val, + formatted_raw_ostream &os) LLVM_OVERRIDE { + bool padding = false; + if (!val.getType()->isVoidTy()) { + os.PadToColumn(50); + padding = true; + os << "; [#uses = " << val.getNumUses(); + if (isa(&val) || isa(&val)) { + // Only print type for instructions where it is not obvious + // from being repeated in its parameters. Might need to be + // extended, but GEPs/PHIs are the most common ones. + os << ", type = " << *val.getType(); + } else if (isa(&val)) { + os << ", size/byte = " + << gDataLayout->getTypeAllocSize(val.getType()->getContainedType(0)); + } + os << ']'; + } + + const Instruction *instr = dyn_cast(&val); + if (!instr) + return; + +#if LDC_LLVM_VER >= 307 + if (const DebugLoc &debugLoc = instr->getDebugLoc()) +#else + const DebugLoc &debugLoc = instr->getDebugLoc(); + if (!debugLoc.isUnknown()) +#endif { - // Find the MDNode which corresponds to the DISubprogram data that described F. + if (!padding) { + os.PadToColumn(50); + padding = true; + os << ';'; + } + os << " [debug line = "; + printDebugLoc(debugLoc, os); + os << ']'; + } + if (const DbgDeclareInst *DDI = dyn_cast(instr)) { #if LDC_LLVM_VER >= 307 - static DISubprogram* FindSubprogram(const Function *F, DebugInfoFinder &Finder) + DILocalVariable *Var(DDI->getVariable()); #else - static MDNode* FindSubprogram(const Function *F, DebugInfoFinder &Finder) + DIVariable Var(DDI->getVariable()); #endif - { + if (!padding) { + os.PadToColumn(50); + os << ";"; + } #if LDC_LLVM_VER >= 307 - for (DISubprogram* Subprogram : Finder.subprograms()) - if (Subprogram->describes(F)) return Subprogram; - return nullptr; + os << " [debug variable = " << Var->getName() << ']'; #else - for (DISubprogram Subprogram : Finder.subprograms()) - if (Subprogram.describes(F)) return Subprogram; - return nullptr; + os << " [debug variable = " << Var.getName() << ']'; #endif + } else if (const DbgValueInst *DVI = dyn_cast(instr)) { +#if LDC_LLVM_VER >= 307 + DILocalVariable *Var(DVI->getVariable()); +#else + DIVariable Var(DVI->getVariable()); +#endif + if (!padding) { + os.PadToColumn(50); + os << ";"; + } +#if LDC_LLVM_VER >= 307 + os << " [debug variable = " << Var->getName() << ']'; +#else + os << " [debug variable = " << Var.getName() << ']'; +#endif + } else if (const CallInst *callinstr = dyn_cast(instr)) { + const Function *F = callinstr->getCalledFunction(); + if (!F) + return; + + StringRef funcName = GetDisplayName(F); + if (!funcName.empty()) { + if (!padding) { + os.PadToColumn(50); + os << ";"; } + os << " [display name = " << funcName << ']'; + } + } else if (const InvokeInst *invokeinstr = dyn_cast(instr)) { + const Function *F = invokeinstr->getCalledFunction(); + if (!F) + return; - static llvm::StringRef GetDisplayName(const Function *F) - { - llvm::DebugInfoFinder Finder; - Finder.processModule(*F->getParent()); -#if LDC_LLVM_VER >= 307 - if (DISubprogram* N = FindSubprogram(F, Finder)) -#else - if (MDNode* N = FindSubprogram(F, Finder)) -#endif - { -#if LDC_LLVM_VER >= 307 - return N->getDisplayName(); -#else - llvm::DISubprogram sub(N); - return sub.getDisplayName(); -#endif - } - return ""; + StringRef funcName = GetDisplayName(F); + if (!funcName.empty()) { + if (!padding) { + os.PadToColumn(50); + os << ";"; } - - public: - void emitFunctionAnnot(const Function* F, formatted_raw_ostream& os) LLVM_OVERRIDE - { - os << "; [#uses = " << F->getNumUses() << ']'; - - // show demangled name - llvm::StringRef funcName = GetDisplayName(F); - if (!funcName.empty()) - os << " [display name = " << funcName << ']'; - - os << '\n'; - } - - void printInfoComment(const Value& val, formatted_raw_ostream& os) LLVM_OVERRIDE - { - bool padding = false; - if (!val.getType()->isVoidTy()) - { - os.PadToColumn(50); - padding = true; - os << "; [#uses = " << val.getNumUses(); - if (isa(&val) || isa(&val)) - { - // Only print type for instructions where it is not obvious - // from being repeated in its parameters. Might need to be - // extended, but GEPs/PHIs are the most common ones. - os << ", type = " << *val.getType(); - } - else if (isa(&val)) - { - os << ", size/byte = " << - gDataLayout->getTypeAllocSize(val.getType()->getContainedType(0)); - } - os << ']'; - } - - const Instruction* instr = dyn_cast(&val); - if (!instr) - return; - -#if LDC_LLVM_VER >= 307 - if (const DebugLoc &debugLoc = instr->getDebugLoc()) -#else - const DebugLoc& debugLoc = instr->getDebugLoc(); - if (!debugLoc.isUnknown()) -#endif - { - if (!padding) - { - os.PadToColumn(50); - padding = true; - os << ';'; - } - os << " [debug line = "; - printDebugLoc(debugLoc, os); - os << ']'; - } - if (const DbgDeclareInst* DDI = dyn_cast(instr)) - { -#if LDC_LLVM_VER >= 307 - DILocalVariable* Var(DDI->getVariable()); -#else - DIVariable Var(DDI->getVariable()); -#endif - if (!padding) - { - os.PadToColumn(50); - os << ";"; - } -#if LDC_LLVM_VER >= 307 - os << " [debug variable = " << Var->getName() << ']'; -#else - os << " [debug variable = " << Var.getName() << ']'; -#endif - } - else if (const DbgValueInst* DVI = dyn_cast(instr)) - { -#if LDC_LLVM_VER >= 307 - DILocalVariable* Var(DVI->getVariable()); -#else - DIVariable Var(DVI->getVariable()); -#endif - if (!padding) - { - os.PadToColumn(50); - os << ";"; - } -#if LDC_LLVM_VER >= 307 - os << " [debug variable = " << Var->getName() << ']'; -#else - os << " [debug variable = " << Var.getName() << ']'; -#endif - } - else if (const CallInst* callinstr = dyn_cast(instr)) - { - const Function* F = callinstr->getCalledFunction(); - if (!F) - return; - - StringRef funcName = GetDisplayName(F); - if (!funcName.empty()) - { - if (!padding) - { - os.PadToColumn(50); - os << ";"; - } - os << " [display name = " << funcName << ']'; - } - } - else if (const InvokeInst* invokeinstr = dyn_cast(instr)) - { - const Function* F = invokeinstr->getCalledFunction(); - if (!F) - return; - - StringRef funcName = GetDisplayName(F); - if (!funcName.empty()) - { - if (!padding) - { - os.PadToColumn(50); - os << ";"; - } - os << " [display name = " << funcName << ']'; - } - } - } - }; + os << " [display name = " << funcName << ']'; + } + } + } +}; } // end of anonymous namespace -void writeModule(llvm::Module* m, std::string filename) -{ - // run optimizer - ldc_optimize_module(m); +void writeModule(llvm::Module *m, std::string filename) { + // run optimizer + ldc_optimize_module(m); - // There is no integrated assembler on AIX because XCOFF is not supported. - // Starting with LLVM 3.5 the integrated assembler can be used with MinGW. - bool const assembleExternally = global.params.output_o && - (NoIntegratedAssembler || - global.params.targetTriple.getOS() == llvm::Triple::AIX); + // There is no integrated assembler on AIX because XCOFF is not supported. + // Starting with LLVM 3.5 the integrated assembler can be used with MinGW. + bool const assembleExternally = + global.params.output_o && + (NoIntegratedAssembler || + global.params.targetTriple.getOS() == llvm::Triple::AIX); - // eventually do our own path stuff, dmd's is a bit strange. - typedef llvm::SmallString<128> LLPath; + // eventually do our own path stuff, dmd's is a bit strange. + typedef llvm::SmallString<128> LLPath; - // write LLVM bitcode - if (global.params.output_bc) { - LLPath bcpath = LLPath(filename); - llvm::sys::path::replace_extension(bcpath, global.bc_ext); - Logger::println("Writing LLVM bitcode to: %s\n", bcpath.c_str()); + // write LLVM bitcode + if (global.params.output_bc) { + LLPath bcpath = LLPath(filename); + llvm::sys::path::replace_extension(bcpath, global.bc_ext); + Logger::println("Writing LLVM bitcode to: %s\n", bcpath.c_str()); #if LDC_LLVM_VER >= 306 - std::error_code errinfo; + std::error_code errinfo; #else - std::string errinfo; + std::string errinfo; #endif - llvm::raw_fd_ostream bos(bcpath.c_str(), errinfo, llvm::sys::fs::F_None); - if (bos.has_error()) - { - error(Loc(), "cannot write LLVM bitcode file '%s': %s", bcpath.c_str(), + llvm::raw_fd_ostream bos(bcpath.c_str(), errinfo, llvm::sys::fs::F_None); + if (bos.has_error()) { + error(Loc(), "cannot write LLVM bitcode file '%s': %s", bcpath.c_str(), #if LDC_LLVM_VER >= 306 errinfo #else errinfo.c_str() #endif ); - fatal(); - } - llvm::WriteBitcodeToFile(m, bos); + fatal(); } + llvm::WriteBitcodeToFile(m, bos); + } - // write LLVM IR - if (global.params.output_ll) { - LLPath llpath = LLPath(filename); - llvm::sys::path::replace_extension(llpath, global.ll_ext); - Logger::println("Writing LLVM asm to: %s\n", llpath.c_str()); + // write LLVM IR + if (global.params.output_ll) { + LLPath llpath = LLPath(filename); + llvm::sys::path::replace_extension(llpath, global.ll_ext); + Logger::println("Writing LLVM asm to: %s\n", llpath.c_str()); #if LDC_LLVM_VER >= 306 - std::error_code errinfo; + std::error_code errinfo; #else - std::string errinfo; + std::string errinfo; #endif - llvm::raw_fd_ostream aos(llpath.c_str(), errinfo, llvm::sys::fs::F_None); - if (aos.has_error()) - { - error(Loc(), "cannot write LLVM asm file '%s': %s", llpath.c_str(), + llvm::raw_fd_ostream aos(llpath.c_str(), errinfo, llvm::sys::fs::F_None); + if (aos.has_error()) { + error(Loc(), "cannot write LLVM asm file '%s': %s", llpath.c_str(), #if LDC_LLVM_VER >= 306 errinfo #else errinfo.c_str() #endif ); - fatal(); - } - AssemblyAnnotator annotator; - m->print(aos, &annotator); + fatal(); + } + AssemblyAnnotator annotator; + m->print(aos, &annotator); + } + + // write native assembly + if (global.params.output_s || assembleExternally) { + LLPath spath = LLPath(filename); + llvm::sys::path::replace_extension(spath, global.s_ext); + if (!global.params.output_s) + llvm::sys::fs::createUniqueFile("ldc-%%%%%%%.s", spath); + + Logger::println("Writing native asm to: %s\n", spath.c_str()); +#if LDC_LLVM_VER >= 306 + std::error_code errinfo; +#else + std::string errinfo; +#endif + { + llvm::raw_fd_ostream out(spath.c_str(), errinfo, llvm::sys::fs::F_None); +#if LDC_LLVM_VER >= 306 + if (!errinfo) +#else + if (errinfo.empty()) +#endif + { + codegenModule(*gTargetMachine, *m, out, + llvm::TargetMachine::CGFT_AssemblyFile); + } else { + error(Loc(), "cannot write native asm: %s", +#if LDC_LLVM_VER >= 306 + errinfo +#else + errinfo.c_str() +#endif + ); + fatal(); + } } - // write native assembly - if (global.params.output_s || assembleExternally) { - LLPath spath = LLPath(filename); - llvm::sys::path::replace_extension(spath, global.s_ext); - if (!global.params.output_s) - llvm::sys::fs::createUniqueFile("ldc-%%%%%%%.s", spath); - - Logger::println("Writing native asm to: %s\n", spath.c_str()); -#if LDC_LLVM_VER >= 306 - std::error_code errinfo; -#else - std::string errinfo; -#endif - { - llvm::raw_fd_ostream out(spath.c_str(), errinfo, llvm::sys::fs::F_None); -#if LDC_LLVM_VER >= 306 - if (!errinfo) -#else - if (errinfo.empty()) -#endif - { - codegenModule(*gTargetMachine, *m, out, llvm::TargetMachine::CGFT_AssemblyFile); - } - else - { - error(Loc(), "cannot write native asm: %s", -#if LDC_LLVM_VER >= 306 - errinfo -#else - errinfo.c_str() -#endif - ); - fatal(); - } - } - - if (assembleExternally) - { - LLPath objpath(filename); - assemble(spath.str(), objpath.str()); - } - - if (!global.params.output_s) - { - llvm::sys::fs::remove(spath.str()); - } + if (assembleExternally) { + LLPath objpath(filename); + assemble(spath.str(), objpath.str()); } - if (global.params.output_o && !assembleExternally) { - LLPath objpath = LLPath(filename); - Logger::println("Writing object file to: %s\n", objpath.c_str()); -#if LDC_LLVM_VER >= 306 - std::error_code errinfo; -#else - std::string errinfo; -#endif - { - llvm::raw_fd_ostream out(objpath.c_str(), errinfo, llvm::sys::fs::F_None); -#if LDC_LLVM_VER >= 306 - if (!errinfo) -#else - if (errinfo.empty()) -#endif - { - codegenModule(*gTargetMachine, *m, out, llvm::TargetMachine::CGFT_ObjectFile); - } - else - { - error(Loc(), "cannot write object file: %s", -#if LDC_LLVM_VER >= 306 - errinfo -#else - errinfo.c_str() -#endif - ); - fatal(); - } - } + if (!global.params.output_s) { + llvm::sys::fs::remove(spath.str()); } + } + + if (global.params.output_o && !assembleExternally) { + LLPath objpath = LLPath(filename); + Logger::println("Writing object file to: %s\n", objpath.c_str()); +#if LDC_LLVM_VER >= 306 + std::error_code errinfo; +#else + std::string errinfo; +#endif + { + llvm::raw_fd_ostream out(objpath.c_str(), errinfo, llvm::sys::fs::F_None); +#if LDC_LLVM_VER >= 306 + if (!errinfo) +#else + if (errinfo.empty()) +#endif + { + codegenModule(*gTargetMachine, *m, out, + llvm::TargetMachine::CGFT_ObjectFile); + } else { + error(Loc(), "cannot write object file: %s", +#if LDC_LLVM_VER >= 306 + errinfo +#else + errinfo.c_str() +#endif + ); + fatal(); + } + } + } } diff --git a/driver/toobj.h b/driver/toobj.h index 03b9ddb8d3..fb193f36d4 100644 --- a/driver/toobj.h +++ b/driver/toobj.h @@ -16,8 +16,10 @@ #include -namespace llvm { class Module; } +namespace llvm { +class Module; +} -void writeModule(llvm::Module* m, std::string filename); +void writeModule(llvm::Module *m, std::string filename); #endif diff --git a/driver/tool.cpp b/driver/tool.cpp index d44b898dc6..9a99f73743 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -11,35 +11,34 @@ #include "mars.h" #include "llvm/Support/Program.h" -int executeToolAndWait(const std::string &tool, std::vector const &args, bool verbose) -{ - // Construct real argument list. - // First entry is the tool itself, last entry must be NULL. - std::vector realargs; - realargs.reserve(args.size() + 2); - realargs.push_back(tool.c_str()); - for (const auto& arg : args) - realargs.push_back(arg.c_str()); - realargs.push_back(NULL); +int executeToolAndWait(const std::string &tool, + std::vector const &args, bool verbose) { + // Construct real argument list. + // First entry is the tool itself, last entry must be NULL. + std::vector realargs; + realargs.reserve(args.size() + 2); + realargs.push_back(tool.c_str()); + for (const auto &arg : args) + realargs.push_back(arg.c_str()); + realargs.push_back(NULL); - // Print command line if requested - if (verbose) - { - // Print it - for (size_t i = 0; i < realargs.size()-1; i++) - fprintf(global.stdmsg, "%s ", realargs[i]); - fprintf(global.stdmsg, "\n"); - fflush(global.stdmsg); - } + // Print command line if requested + if (verbose) { + // Print it + for (size_t i = 0; i < realargs.size() - 1; i++) + fprintf(global.stdmsg, "%s ", realargs[i]); + fprintf(global.stdmsg, "\n"); + fflush(global.stdmsg); + } - // Execute tool. - std::string errstr; - if (int status = llvm::sys::ExecuteAndWait(tool, &realargs[0], NULL, NULL, 0, 0, &errstr)) - { - error(Loc(), "%s failed with status: %d", tool.c_str(), status); - if (!errstr.empty()) - error(Loc(), "message: %s", errstr.c_str()); - return status; - } - return 0; + // Execute tool. + std::string errstr; + if (int status = llvm::sys::ExecuteAndWait(tool, &realargs[0], NULL, NULL, 0, + 0, &errstr)) { + error(Loc(), "%s failed with status: %d", tool.c_str(), status); + if (!errstr.empty()) + error(Loc(), "message: %s", errstr.c_str()); + return status; + } + return 0; } diff --git a/driver/tool.h b/driver/tool.h index 18668e3895..d4dc5f858d 100644 --- a/driver/tool.h +++ b/driver/tool.h @@ -1,4 +1,5 @@ -//===-- driver/tool.h - External tool invocation helpers ---------*- C++ -*-===// +//===-- driver/tool.h - External tool invocation helpers ---------*- C++ +//-*-===// // // LDC – the LLVM D compiler // @@ -18,6 +19,8 @@ #include #include -int executeToolAndWait(const std::string &tool, std::vector const &args, bool verbose = false); +int executeToolAndWait(const std::string &tool, + std::vector const &args, + bool verbose = false); #endif diff --git a/gen/aa.cpp b/gen/aa.cpp index 09d211ae67..229fd25ea3 100644 --- a/gen/aa.cpp +++ b/gen/aa.cpp @@ -23,185 +23,190 @@ #include "ir/irmodule.h" // returns the keytype typeinfo -static LLValue* to_keyti(DValue* aa) -{ - // keyti param - assert(aa->type->toBasetype()->ty == Taarray); - TypeAArray * aatype = static_cast(aa->type->toBasetype()); - return DtoTypeInfoOf(aatype->index, false); +static LLValue *to_keyti(DValue *aa) { + // keyti param + assert(aa->type->toBasetype()->ty == Taarray); + TypeAArray *aatype = static_cast(aa->type->toBasetype()); + return DtoTypeInfoOf(aatype->index, false); } ///////////////////////////////////////////////////////////////////////////////////// -DValue* DtoAAIndex(Loc& loc, Type* type, DValue* aa, DValue* key, bool lvalue) -{ - // D2: - // call: - // extern(C) void* _aaGetY(AA* aa, TypeInfo aati, size_t valuesize, void* pkey) - // or - // extern(C) void* _aaInX(AA aa*, TypeInfo keyti, void* pkey) +DValue *DtoAAIndex(Loc &loc, Type *type, DValue *aa, DValue *key, bool lvalue) { + // D2: + // call: + // extern(C) void* _aaGetY(AA* aa, TypeInfo aati, size_t valuesize, void* + // pkey) + // or + // extern(C) void* _aaInX(AA aa*, TypeInfo keyti, void* pkey) - // first get the runtime function - llvm::Function* func = LLVM_D_GetRuntimeFunction(loc, gIR->module, lvalue?"_aaGetY":"_aaInX"); - LLFunctionType* funcTy = func->getFunctionType(); + // first get the runtime function + llvm::Function *func = LLVM_D_GetRuntimeFunction( + loc, gIR->module, lvalue ? "_aaGetY" : "_aaInX"); + LLFunctionType *funcTy = func->getFunctionType(); - // aa param - LLValue* aaval = lvalue ? aa->getLVal() : aa->getRVal(); - aaval = DtoBitCast(aaval, funcTy->getParamType(0)); + // aa param + LLValue *aaval = lvalue ? aa->getLVal() : aa->getRVal(); + aaval = DtoBitCast(aaval, funcTy->getParamType(0)); - // pkey param - LLValue* pkey = makeLValue(loc, key); - pkey = DtoBitCast(pkey, funcTy->getParamType(lvalue ? 3 : 2)); + // pkey param + LLValue *pkey = makeLValue(loc, key); + pkey = DtoBitCast(pkey, funcTy->getParamType(lvalue ? 3 : 2)); - // call runtime - LLValue* ret; - if (lvalue) { - LLValue* rawAATI = DtoTypeInfoOf(aa->type->unSharedOf()->mutableOf(), false); - LLValue* castedAATI = DtoBitCast(rawAATI, funcTy->getParamType(1)); - LLValue* valsize = DtoConstSize_t(getTypePaddedSize(DtoType(type))); - ret = gIR->CreateCallOrInvoke(func, aaval, castedAATI, valsize, pkey, "aa.index").getInstruction(); - } else { - LLValue* keyti = DtoBitCast(to_keyti(aa), funcTy->getParamType(1)); - ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.index").getInstruction(); - } + // call runtime + LLValue *ret; + if (lvalue) { + LLValue *rawAATI = + DtoTypeInfoOf(aa->type->unSharedOf()->mutableOf(), false); + LLValue *castedAATI = DtoBitCast(rawAATI, funcTy->getParamType(1)); + LLValue *valsize = DtoConstSize_t(getTypePaddedSize(DtoType(type))); + ret = gIR->CreateCallOrInvoke(func, aaval, castedAATI, valsize, pkey, + "aa.index") + .getInstruction(); + } else { + LLValue *keyti = DtoBitCast(to_keyti(aa), funcTy->getParamType(1)); + ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.index") + .getInstruction(); + } - // cast return value - LLType* targettype = DtoPtrToType(type); - if (ret->getType() != targettype) - ret = DtoBitCast(ret, targettype); + // cast return value + LLType *targettype = DtoPtrToType(type); + if (ret->getType() != targettype) + ret = DtoBitCast(ret, targettype); - // Only check bounds for rvalues ('aa[key]'). - // Lvalue use ('aa[key] = value') auto-adds an element. - if (!lvalue && gIR->emitArrayBoundsChecks()) { - llvm::BasicBlock* failbb = llvm::BasicBlock::Create(gIR->context(), "aaboundscheckfail", gIR->topfunc()); - llvm::BasicBlock* okbb = llvm::BasicBlock::Create(gIR->context(), "aaboundsok", gIR->topfunc()); + // Only check bounds for rvalues ('aa[key]'). + // Lvalue use ('aa[key] = value') auto-adds an element. + if (!lvalue && gIR->emitArrayBoundsChecks()) { + llvm::BasicBlock *failbb = llvm::BasicBlock::Create( + gIR->context(), "aaboundscheckfail", gIR->topfunc()); + llvm::BasicBlock *okbb = + llvm::BasicBlock::Create(gIR->context(), "aaboundsok", gIR->topfunc()); - LLValue* nullaa = LLConstant::getNullValue(ret->getType()); - LLValue* cond = gIR->ir->CreateICmpNE(nullaa, ret, "aaboundscheck"); - gIR->ir->CreateCondBr(cond, okbb, failbb); + LLValue *nullaa = LLConstant::getNullValue(ret->getType()); + LLValue *cond = gIR->ir->CreateICmpNE(nullaa, ret, "aaboundscheck"); + gIR->ir->CreateCondBr(cond, okbb, failbb); - // set up failbb to call the array bounds error runtime function + // set up failbb to call the array bounds error runtime function - gIR->scope() = IRScope(failbb); + gIR->scope() = IRScope(failbb); - llvm::Function* errorfn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arraybounds"); - gIR->CreateCallOrInvoke( - errorfn, - DtoModuleFileName(gIR->func()->decl->getModule(), loc), - DtoConstUint(loc.linnum) - ); + llvm::Function *errorfn = + LLVM_D_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(); + // the function does not return + gIR->ir->CreateUnreachable(); - // if ok, proceed in okbb - gIR->scope() = IRScope(okbb); - } - return new DVarValue(type, ret); + // if ok, proceed in okbb + gIR->scope() = IRScope(okbb); + } + return new DVarValue(type, ret); } ///////////////////////////////////////////////////////////////////////////////////// -DValue* DtoAAIn(Loc& loc, Type* type, DValue* aa, DValue* key) -{ - // D1: - // call: - // extern(C) void* _aaIn(AA aa*, TypeInfo keyti, void* pkey) +DValue *DtoAAIn(Loc &loc, Type *type, DValue *aa, DValue *key) { + // D1: + // call: + // extern(C) void* _aaIn(AA aa*, TypeInfo keyti, void* pkey) - // D2: - // call: - // extern(C) void* _aaInX(AA aa*, TypeInfo keyti, void* pkey) + // D2: + // call: + // extern(C) void* _aaInX(AA aa*, TypeInfo keyti, void* pkey) - // first get the runtime function - llvm::Function* func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_aaInX"); - LLFunctionType* funcTy = func->getFunctionType(); + // first get the runtime function + llvm::Function *func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_aaInX"); + LLFunctionType *funcTy = func->getFunctionType(); - IF_LOG Logger::cout() << "_aaIn = " << *func << '\n'; + IF_LOG Logger::cout() << "_aaIn = " << *func << '\n'; - // aa param - LLValue* aaval = aa->getRVal(); - IF_LOG { - Logger::cout() << "aaval: " << *aaval << '\n'; - Logger::cout() << "totype: " << *funcTy->getParamType(0) << '\n'; - } - aaval = DtoBitCast(aaval, funcTy->getParamType(0)); + // aa param + LLValue *aaval = aa->getRVal(); + IF_LOG { + Logger::cout() << "aaval: " << *aaval << '\n'; + Logger::cout() << "totype: " << *funcTy->getParamType(0) << '\n'; + } + aaval = DtoBitCast(aaval, funcTy->getParamType(0)); - // keyti param - LLValue* keyti = to_keyti(aa); - keyti = DtoBitCast(keyti, funcTy->getParamType(1)); + // keyti param + LLValue *keyti = to_keyti(aa); + keyti = DtoBitCast(keyti, funcTy->getParamType(1)); - // pkey param - LLValue* pkey = makeLValue(loc, key); - pkey = DtoBitCast(pkey, getVoidPtrType()); + // pkey param + LLValue *pkey = makeLValue(loc, key); + pkey = DtoBitCast(pkey, getVoidPtrType()); - // call runtime - LLValue* ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.in").getInstruction(); + // call runtime + LLValue *ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.in") + .getInstruction(); - // cast return value - LLType* targettype = DtoType(type); - if (ret->getType() != targettype) - ret = DtoBitCast(ret, targettype); + // cast return value + LLType *targettype = DtoType(type); + if (ret->getType() != targettype) + ret = DtoBitCast(ret, targettype); - return new DImValue(type, ret); + return new DImValue(type, ret); } ///////////////////////////////////////////////////////////////////////////////////// -DValue *DtoAARemove(Loc& loc, DValue* aa, DValue* key) -{ - // D1: - // call: - // extern(C) void _aaDel(AA aa, TypeInfo keyti, void* pkey) +DValue *DtoAARemove(Loc &loc, DValue *aa, DValue *key) { + // D1: + // call: + // extern(C) void _aaDel(AA aa, TypeInfo keyti, void* pkey) - // D2: - // call: - // extern(C) bool _aaDelX(AA aa, TypeInfo keyti, void* pkey) + // D2: + // call: + // extern(C) bool _aaDelX(AA aa, TypeInfo keyti, void* pkey) - // first get the runtime function - llvm::Function* func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_aaDelX"); - LLFunctionType* funcTy = func->getFunctionType(); + // first get the runtime function + llvm::Function *func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_aaDelX"); + LLFunctionType *funcTy = func->getFunctionType(); - IF_LOG Logger::cout() << "_aaDel = " << *func << '\n'; + IF_LOG Logger::cout() << "_aaDel = " << *func << '\n'; - // aa param - LLValue* aaval = aa->getRVal(); - IF_LOG { - Logger::cout() << "aaval: " << *aaval << '\n'; - Logger::cout() << "totype: " << *funcTy->getParamType(0) << '\n'; - } - aaval = DtoBitCast(aaval, funcTy->getParamType(0)); + // aa param + LLValue *aaval = aa->getRVal(); + IF_LOG { + Logger::cout() << "aaval: " << *aaval << '\n'; + Logger::cout() << "totype: " << *funcTy->getParamType(0) << '\n'; + } + aaval = DtoBitCast(aaval, funcTy->getParamType(0)); - // keyti param - LLValue* keyti = to_keyti(aa); - keyti = DtoBitCast(keyti, funcTy->getParamType(1)); + // keyti param + LLValue *keyti = to_keyti(aa); + keyti = DtoBitCast(keyti, funcTy->getParamType(1)); - // pkey param - LLValue* pkey = makeLValue(loc, key); - pkey = DtoBitCast(pkey, funcTy->getParamType(2)); + // pkey param + LLValue *pkey = makeLValue(loc, key); + pkey = DtoBitCast(pkey, funcTy->getParamType(2)); - // call runtime - LLCallSite call = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey); + // call runtime + LLCallSite call = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey); - return new DImValue(Type::tbool, call.getInstruction()); + return new DImValue(Type::tbool, call.getInstruction()); } ///////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoAAEquals(Loc& loc, TOK op, DValue* l, DValue* r) -{ - Type* t = l->getType()->toBasetype(); - assert(t == r->getType()->toBasetype() && "aa equality is only defined for aas of same type"); - llvm::Function* func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_aaEqual"); - LLFunctionType* funcTy = func->getFunctionType(); +LLValue *DtoAAEquals(Loc &loc, TOK op, DValue *l, DValue *r) { + Type *t = l->getType()->toBasetype(); + assert(t == r->getType()->toBasetype() && + "aa equality is only defined for aas of same type"); + llvm::Function *func = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_aaEqual"); + LLFunctionType *funcTy = func->getFunctionType(); - LLValue* aaval = DtoBitCast(l->getRVal(), funcTy->getParamType(1)); - LLValue* abval = DtoBitCast(r->getRVal(), funcTy->getParamType(2)); - LLValue* aaTypeInfo = DtoTypeInfoOf(t); - LLValue* res = gIR->CreateCallOrInvoke(func, aaTypeInfo, aaval, abval, "aaEqRes").getInstruction(); - res = gIR->ir->CreateICmpNE(res, DtoConstInt(0)); - if (op == TOKnotequal) - res = gIR->ir->CreateNot(res); - return res; + LLValue *aaval = DtoBitCast(l->getRVal(), funcTy->getParamType(1)); + LLValue *abval = DtoBitCast(r->getRVal(), funcTy->getParamType(2)); + LLValue *aaTypeInfo = DtoTypeInfoOf(t); + LLValue *res = + gIR->CreateCallOrInvoke(func, aaTypeInfo, aaval, abval, "aaEqRes") + .getInstruction(); + res = gIR->ir->CreateICmpNE(res, DtoConstInt(0)); + if (op == TOKnotequal) + res = gIR->ir->CreateNot(res); + return res; } - - diff --git a/gen/aa.h b/gen/aa.h index b1185b518b..0ea6562129 100644 --- a/gen/aa.h +++ b/gen/aa.h @@ -20,11 +20,13 @@ enum TOK; class DValue; struct Loc; class Type; -namespace llvm { class Value; } +namespace llvm { +class Value; +} -DValue* DtoAAIndex(Loc& loc, Type* type, DValue* aa, DValue* key, bool lvalue); -DValue* DtoAAIn(Loc& loc, Type* type, DValue* aa, DValue* key); -DValue* DtoAARemove(Loc& loc, DValue* aa, DValue* key); -llvm::Value* DtoAAEquals(Loc& loc, TOK op, DValue* l, DValue* r); +DValue *DtoAAIndex(Loc &loc, Type *type, DValue *aa, DValue *key, bool lvalue); +DValue *DtoAAIn(Loc &loc, Type *type, DValue *aa, DValue *key); +DValue *DtoAARemove(Loc &loc, DValue *aa, DValue *key); +llvm::Value *DtoAAEquals(Loc &loc, TOK op, DValue *l, DValue *r); #endif // LDC_GEN_AA_H diff --git a/gen/abi-aarch64.cpp b/gen/abi-aarch64.cpp old mode 100755 new mode 100644 index 433f773b53..74035b41e9 --- a/gen/abi-aarch64.cpp +++ b/gen/abi-aarch64.cpp @@ -16,125 +16,120 @@ #include "gen/abi-generic.h" #include "gen/abi-aarch64.h" -struct AArch64TargetABI : TargetABI -{ - llvm::CallingConv::ID callingConv(LINK l) - { - switch (l) - { - case LINKc: - case LINKcpp: - case LINKpascal: - case LINKwindows: - case LINKd: - case LINKdefault: - return llvm::CallingConv::C; - default: - llvm_unreachable("Unhandled D linkage type."); - } +struct AArch64TargetABI : TargetABI { + llvm::CallingConv::ID callingConv(LINK l) { + switch (l) { + case LINKc: + case LINKcpp: + case LINKpascal: + case LINKwindows: + case LINKd: + case LINKdefault: + return llvm::CallingConv::C; + default: + llvm_unreachable("Unhandled D linkage type."); } + } - bool returnInArg(TypeFunction* tf) - { - if (tf->isref) - return false; + bool returnInArg(TypeFunction *tf) { + if (tf->isref) + return false; - // Return structs and static arrays on the stack. The latter is needed - // because otherwise LLVM tries to actually return the array in a number - // of physical registers, which leads, depending on the target, to - // either horrendous codegen or backend crashes. - Type* rt = tf->next->toBasetype(); - return (rt->ty == Tstruct || rt->ty == Tsarray); + // Return structs and static arrays on the stack. The latter is needed + // because otherwise LLVM tries to actually return the array in a number + // of physical registers, which leads, depending on the target, to + // either horrendous codegen or backend crashes. + Type *rt = tf->next->toBasetype(); + return (rt->ty == Tstruct || rt->ty == Tsarray); + } + + bool passByVal(Type *t) { return t->toBasetype()->ty == Tstruct; } + + void rewriteFunctionType(TypeFunction *t, IrFuncTy &fty) { + for (auto arg : fty.args) { + if (!arg->byref) + rewriteArgument(fty, *arg); } + } - bool passByVal(Type* t) - { - return t->toBasetype()->ty == Tstruct; - } + void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) { + // FIXME + } - void rewriteFunctionType(TypeFunction* t, IrFuncTy& fty) - { - for (auto arg : fty.args) - { - if (!arg->byref) - rewriteArgument(fty, *arg); - } - } + /** + * The AACPS64 uses a special native va_list type: + * + * typedef struct __va_list { + * void *__stack; // next stack param + * void *__gr_top; // end of GP arg reg save area + * void *__vr_top; // end of FP/SIMD arg reg save area + * int __gr_offs; // offset from __gr_top to next GP register arg + * int __vr_offs; // offset from __vr_top to next FP/SIMD register arg + * } va_list; + * + * In druntime, the struct is defined as core.stdc.stdarg.__va_list; the + * actually used + * core.stdc.stdarg.va_list type is a raw char* pointer though to achieve byref + * semantics. + * This requires a little bit of compiler magic in the following + * implementations. + */ - void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg) - { - // FIXME - } + LLType *getValistType() { + LLType *intType = LLType::getInt32Ty(gIR->context()); + LLType *voidPointerType = getVoidPtrType(); - /** - * The AACPS64 uses a special native va_list type: - * - * typedef struct __va_list { - * void *__stack; // next stack param - * void *__gr_top; // end of GP arg reg save area - * void *__vr_top; // end of FP/SIMD arg reg save area - * int __gr_offs; // offset from __gr_top to next GP register arg - * int __vr_offs; // offset from __vr_top to next FP/SIMD register arg - * } va_list; - * - * In druntime, the struct is defined as core.stdc.stdarg.__va_list; the actually used - * core.stdc.stdarg.va_list type is a raw char* pointer though to achieve byref semantics. - * This requires a little bit of compiler magic in the following implementations. - */ + std::vector parts; // struct __va_list { + parts.push_back(voidPointerType); // void *__stack; + parts.push_back(voidPointerType); // void *__gr_top; + parts.push_back(voidPointerType); // void *__vr_top; + parts.push_back(intType); // int __gr_offs; + parts.push_back(intType); // int __vr_offs; }; - LLType* getValistType() { - LLType* intType = LLType::getInt32Ty(gIR->context()); - LLType* voidPointerType = getVoidPtrType(); + return LLStructType::get(gIR->context(), parts); + } - std::vector parts; // struct __va_list { - parts.push_back(voidPointerType); // void *__stack; - parts.push_back(voidPointerType); // void *__gr_top; - parts.push_back(voidPointerType); // void *__vr_top; - parts.push_back(intType); // int __gr_offs; - parts.push_back(intType); // int __vr_offs; }; + LLValue *prepareVaStart(LLValue *pAp) { + // Since the user only created a char* pointer (ap) on the stack before + // invoking va_start, + // we first need to allocate the actual __va_list struct and set 'ap' to its + // address. + LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem"); + valistmem = DtoBitCast(valistmem, getVoidPtrType()); + DtoStore(valistmem, pAp); // ap = (void*)__va_list_mem - return LLStructType::get(gIR->context(), parts); - } + // pass a void* pointer to the actual struct to LLVM's va_start intrinsic + return valistmem; + } - LLValue* prepareVaStart(LLValue* pAp) { - // Since the user only created a char* pointer (ap) on the stack before invoking va_start, - // we first need to allocate the actual __va_list struct and set 'ap' to its address. - LLValue* valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem"); - valistmem = DtoBitCast(valistmem, getVoidPtrType()); - DtoStore(valistmem, pAp); // ap = (void*)__va_list_mem + void vaCopy(LLValue *pDest, LLValue *src) { + // Analog to va_start, we need to allocate a new __va_list struct on the + // stack, + // fill it with a bitcopy of the source struct... + src = DtoLoad( + DtoBitCast(src, getValistType()->getPointerTo())); // *(__va_list*)src + LLValue *valistmem = DtoAllocaDump(src, 0, "__va_list_mem"); + // ... and finally set the passed 'dest' char* pointer to the new struct's + // address. + DtoStore(DtoBitCast(valistmem, getVoidPtrType()), + DtoBitCast(pDest, getPtrToType(getVoidPtrType()))); + } - // pass a void* pointer to the actual struct to LLVM's va_start intrinsic - return valistmem; - } + LLValue *prepareVaArg(LLValue *pAp) { + // pass a void* pointer to the actual __va_list struct to LLVM's va_arg + // intrinsic + return DtoLoad(pAp); + } - void vaCopy(LLValue* pDest, LLValue* src) { - // Analog to va_start, we need to allocate a new __va_list struct on the stack, - // fill it with a bitcopy of the source struct... - src = DtoLoad(DtoBitCast(src, getValistType()->getPointerTo())); // *(__va_list*)src - LLValue* valistmem = DtoAllocaDump(src, 0, "__va_list_mem"); - // ... and finally set the passed 'dest' char* pointer to the new struct's address. - DtoStore(DtoBitCast(valistmem, getVoidPtrType()), - DtoBitCast(pDest, getPtrToType(getVoidPtrType()))); - } - - LLValue* prepareVaArg(LLValue* pAp) - { - // pass a void* pointer to the actual __va_list struct to LLVM's va_arg intrinsic - return DtoLoad(pAp); - } - - Type* vaListType() { - // We need to pass the actual va_list type for correct mangling. Simply - // using TypeIdentifier here is a bit wonky but works, as long as the name - // is actually available in the scope (this is what DMD does, so if a better - // solution is found there, this should be adapted). - return (new TypeIdentifier(Loc(), - Identifier::idPool("__va_list_tag")))->pointerTo(); - } + Type *vaListType() { + // We need to pass the actual va_list type for correct mangling. Simply + // using TypeIdentifier here is a bit wonky but works, as long as the name + // is actually available in the scope (this is what DMD does, so if a better + // solution is found there, this should be adapted). + return (new TypeIdentifier(Loc(), Identifier::idPool("__va_list_tag"))) + ->pointerTo(); + } }; // The public getter for abi.cpp -TargetABI* getAArch64TargetABI() -{ - return new AArch64TargetABI(); -} \ No newline at end of file +TargetABI *getAArch64TargetABI() { return new AArch64TargetABI(); } \ No newline at end of file diff --git a/gen/abi-aarch64.h b/gen/abi-aarch64.h old mode 100755 new mode 100644 index 047338ac1e..6fa3b05a80 --- a/gen/abi-aarch64.h +++ b/gen/abi-aarch64.h @@ -16,6 +16,6 @@ struct TargetABI; -TargetABI* getAArch64TargetABI(); +TargetABI *getAArch64TargetABI(); #endif diff --git a/gen/abi-generic.h b/gen/abi-generic.h index c4bf2c0613..6c181f6378 100644 --- a/gen/abi-generic.h +++ b/gen/abi-generic.h @@ -21,90 +21,88 @@ #include "gen/structs.h" #include "gen/tollvm.h" -struct LLTypeMemoryLayout -{ - // Structs and static arrays are folded recursively to scalars or anonymous structs. - // Pointer types are folded to an integer type. - static LLType* fold(LLType* type) - { - // T* => integer - if (type->isPointerTy()) - return LLIntegerType::get(gIR->context(), getTypeBitSize(type)); +struct LLTypeMemoryLayout { + // Structs and static arrays are folded recursively to scalars or anonymous + // structs. + // Pointer types are folded to an integer type. + static LLType *fold(LLType *type) { + // T* => integer + if (type->isPointerTy()) + return LLIntegerType::get(gIR->context(), getTypeBitSize(type)); - if (LLStructType* structType = isaStruct(type)) - { - unsigned numElements = structType->getNumElements(); + if (LLStructType *structType = isaStruct(type)) { + unsigned numElements = structType->getNumElements(); - // fold each element - std::vector elements; - elements.reserve(numElements); - for (unsigned i = 0; i < numElements; ++i) - elements.push_back(fold(structType->getElementType(i))); + // fold each element + std::vector elements; + elements.reserve(numElements); + for (unsigned i = 0; i < numElements; ++i) + elements.push_back(fold(structType->getElementType(i))); - // single element? then discard wrapping struct - if (numElements == 1) - return elements[0]; + // single element? then discard wrapping struct + if (numElements == 1) + return elements[0]; - return LLStructType::get(gIR->context(), elements, structType->isPacked()); - } - - if (LLArrayType* arrayType = isaArray(type)) - { - unsigned numElements = arrayType->getNumElements(); - LLType* foldedElementType = fold(arrayType->getElementType()); - - // single element? then fold to scalar - if (numElements == 1) - return foldedElementType; - - // otherwise: convert to struct of N folded elements - std::vector elements(numElements, foldedElementType); - return LLStructType::get(gIR->context(), elements); - } - - return type; + return LLStructType::get(gIR->context(), elements, + structType->isPacked()); } - // Checks two LLVM types for memory-layout equivalency. - static bool typesAreEquivalent(LLType* a, LLType* b) - { - if (a == b) - return true; - if (!a || !b) - return false; + if (LLArrayType *arrayType = isaArray(type)) { + unsigned numElements = arrayType->getNumElements(); + LLType *foldedElementType = fold(arrayType->getElementType()); - return fold(a) == fold(b); + // single element? then fold to scalar + if (numElements == 1) + return foldedElementType; + + // otherwise: convert to struct of N folded elements + std::vector elements(numElements, foldedElementType); + return LLStructType::get(gIR->context(), elements); } + + return type; + } + + // Checks two LLVM types for memory-layout equivalency. + static bool typesAreEquivalent(LLType *a, LLType *b) { + if (a == b) + return true; + if (!a || !b) + return false; + + return fold(a) == fold(b); + } }; ////////////////////////////////////////////////////////////////////////////// /// Removes padding fields for (non-union-containing!) structs struct RemoveStructPadding : ABIRewrite { - /// get a rewritten value back to its original form - LLValue* get(Type* dty, LLValue* v) { - LLValue* lval = DtoAlloca(dty, ".rewritetmp"); - getL(dty, v, lval); - return lval; - } + /// get a rewritten value back to its original form + LLValue *get(Type *dty, LLValue *v) { + LLValue *lval = DtoAlloca(dty, ".rewritetmp"); + getL(dty, v, lval); + return lval; + } - /// get a rewritten value back to its original form and store result in provided lvalue - void getL(Type* dty, LLValue* v, LLValue* lval) { - // Make sure the padding is zero, so struct comparisons work. - // TODO: Only do this if there's padding, and/or only initialize padding. - DtoMemSetZero(lval, DtoConstSize_t(getTypePaddedSize(DtoType(dty)))); - DtoPaddedStruct(dty->toBasetype(), v, lval); - } + /// get a rewritten value back to its original form and store result in + /// provided lvalue + void getL(Type *dty, LLValue *v, LLValue *lval) { + // Make sure the padding is zero, so struct comparisons work. + // TODO: Only do this if there's padding, and/or only initialize padding. + DtoMemSetZero(lval, DtoConstSize_t(getTypePaddedSize(DtoType(dty)))); + DtoPaddedStruct(dty->toBasetype(), v, lval); + } - /// put out rewritten value - LLValue* put(DValue* v) { - return DtoUnpaddedStruct(v->getType()->toBasetype(), v->getRVal()); - } + /// put out rewritten value + LLValue *put(DValue *v) { + return DtoUnpaddedStruct(v->getType()->toBasetype(), v->getRVal()); + } - /// return the transformed type for this rewrite - LLType* type(Type* dty, LLType* t) { - return DtoUnpaddedStructType(dty->toBasetype()); - } + /// return the transformed type for this rewrite + LLType *type(Type *dty, LLType *t) { + return DtoUnpaddedStructType(dty->toBasetype()); + } }; ////////////////////////////////////////////////////////////////////////////// @@ -113,69 +111,58 @@ struct RemoveStructPadding : ABIRewrite { * Rewrites any parameter to an integer of the same or next bigger size via * bit-casting. */ -struct IntegerRewrite : ABIRewrite -{ - static LLType* getIntegerType(unsigned minSizeInBytes) - { - if (minSizeInBytes > 8) - return NULL; +struct IntegerRewrite : ABIRewrite { + static LLType *getIntegerType(unsigned minSizeInBytes) { + if (minSizeInBytes > 8) + return NULL; - unsigned size = minSizeInBytes; - switch (minSizeInBytes) { - case 0: - size = 1; - break; - case 3: - size = 4; - break; - case 5: - case 6: - case 7: - size = 8; - break; - default: - break; - } - - return LLIntegerType::get(gIR->context(), size * 8); + unsigned size = minSizeInBytes; + switch (minSizeInBytes) { + case 0: + size = 1; + break; + case 3: + size = 4; + break; + case 5: + case 6: + case 7: + size = 8; + break; + default: + break; } - static bool isObsoleteFor(LLType* llType) - { - if (!llType->isSized()) // e.g., opaque types - { - IF_LOG Logger::cout() << "IntegerRewrite: not rewriting non-sized type " - << *llType << '\n'; - return true; - } + return LLIntegerType::get(gIR->context(), size * 8); + } - LLType* integerType = getIntegerType(getTypeStoreSize(llType)); - return LLTypeMemoryLayout::typesAreEquivalent(llType, integerType); + static bool isObsoleteFor(LLType *llType) { + if (!llType->isSized()) // e.g., opaque types + { + IF_LOG Logger::cout() << "IntegerRewrite: not rewriting non-sized type " + << *llType << '\n'; + return true; } - LLValue* get(Type* dty, LLValue* v) - { - LLValue* integerDump = DtoAllocaDump(v, dty, ".IntegerRewrite_dump"); - LLType* type = DtoType(dty); - return loadFromMemory(integerDump, type, ".IntegerRewrite_getResult"); - } + LLType *integerType = getIntegerType(getTypeStoreSize(llType)); + return LLTypeMemoryLayout::typesAreEquivalent(llType, integerType); + } - void getL(Type* dty, LLValue* v, LLValue* lval) - { - storeToMemory(v, lval); - } + LLValue *get(Type *dty, LLValue *v) { + LLValue *integerDump = DtoAllocaDump(v, dty, ".IntegerRewrite_dump"); + LLType *type = DtoType(dty); + return loadFromMemory(integerDump, type, ".IntegerRewrite_getResult"); + } - LLValue* put(DValue* dv) - { - LLValue* address = getAddressOf(dv); - LLType* integerType = getIntegerType(dv->getType()->size()); - return loadFromMemory(address, integerType, ".IntegerRewrite_putResult"); - } + void getL(Type *dty, LLValue *v, LLValue *lval) { storeToMemory(v, lval); } - LLType* type(Type* t, LLType*) - { - return getIntegerType(t->size()); - } + LLValue *put(DValue *dv) { + LLValue *address = getAddressOf(dv); + LLType *integerType = getIntegerType(dv->getType()->size()); + return loadFromMemory(address, integerType, ".IntegerRewrite_putResult"); + } + + LLType *type(Type *t, LLType *) { return getIntegerType(t->size()); } }; ////////////////////////////////////////////////////////////////////////////// @@ -193,41 +180,32 @@ struct IntegerRewrite : ABIRewrite * as a copy on the function arguments stack. In this case, there's no need to * pass an explicit pointer; the address is implicit. */ -struct ExplicitByvalRewrite : ABIRewrite -{ - const size_t alignment; +struct ExplicitByvalRewrite : ABIRewrite { + const size_t alignment; - ExplicitByvalRewrite(size_t alignment = 16) : alignment(alignment) - { } + ExplicitByvalRewrite(size_t alignment = 16) : alignment(alignment) {} - LLValue* get(Type* dty, LLValue* v) - { - return DtoLoad(v, ".ExplicitByvalRewrite_getResult"); + LLValue *get(Type *dty, LLValue *v) { + return DtoLoad(v, ".ExplicitByvalRewrite_getResult"); + } + + void getL(Type *dty, LLValue *v, LLValue *lval) { DtoAggrCopy(lval, v); } + + LLValue *put(DValue *v) { + if (DtoIsPassedByRef(v->getType())) { + LLValue *originalPointer = v->getRVal(); + LLType *type = originalPointer->getType()->getPointerElementType(); + LLValue *copyForCallee = + DtoRawAlloca(type, alignment, ".ExplicitByvalRewrite_putResult"); + DtoAggrCopy(copyForCallee, originalPointer); + return copyForCallee; } - void getL(Type* dty, LLValue* v, LLValue* lval) - { - DtoAggrCopy(lval, v); - } + return DtoAllocaDump(v->getRVal(), alignment, + ".ExplicitByvalRewrite_putResult"); + } - LLValue* put(DValue* v) - { - if (DtoIsPassedByRef(v->getType())) - { - LLValue* originalPointer = v->getRVal(); - LLType* type = originalPointer->getType()->getPointerElementType(); - LLValue* copyForCallee = DtoRawAlloca(type, alignment, ".ExplicitByvalRewrite_putResult"); - DtoAggrCopy(copyForCallee, originalPointer); - return copyForCallee; - } - - return DtoAllocaDump(v->getRVal(), alignment, ".ExplicitByvalRewrite_putResult"); - } - - LLType* type(Type* dty, LLType* t) - { - return DtoPtrToType(dty); - } + LLType *type(Type *dty, LLType *t) { return DtoPtrToType(dty); } }; #endif diff --git a/gen/abi-mips64.cpp b/gen/abi-mips64.cpp index 9b5fe98132..e076f6ab59 100644 --- a/gen/abi-mips64.cpp +++ b/gen/abi-mips64.cpp @@ -21,56 +21,48 @@ #include "gen/tollvm.h" struct MIPS64TargetABI : TargetABI { - ExplicitByvalRewrite byvalRewrite; - IntegerRewrite integerRewrite; - const bool Is64Bit; + ExplicitByvalRewrite byvalRewrite; + IntegerRewrite integerRewrite; + const bool Is64Bit; - MIPS64TargetABI(const bool Is64Bit) : Is64Bit(Is64Bit) - { } + MIPS64TargetABI(const bool Is64Bit) : Is64Bit(Is64Bit) {} - bool returnInArg(TypeFunction* tf) - { - if (tf->isref) - return false; + bool returnInArg(TypeFunction *tf) { + if (tf->isref) + return false; - // Return structs and static arrays on the stack. The latter is needed - // because otherwise LLVM tries to actually return the array in a number - // of physical registers, which leads, depending on the target, to - // either horrendous codegen or backend crashes. - Type* rt = tf->next->toBasetype(); - return (rt->ty == Tstruct || rt->ty == Tsarray); + // Return structs and static arrays on the stack. The latter is needed + // because otherwise LLVM tries to actually return the array in a number + // of physical registers, which leads, depending on the target, to + // either horrendous codegen or backend crashes. + Type *rt = tf->next->toBasetype(); + return (rt->ty == Tstruct || rt->ty == Tsarray); + } + + bool passByVal(Type *t) { + TY ty = t->toBasetype()->ty; + return ty == Tstruct || ty == Tsarray; + } + + void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) { + for (auto arg : fty.args) { + if (!arg->byref) + rewriteArgument(fty, *arg); } + } - bool passByVal(Type* t) - { - TY ty = t->toBasetype()->ty; - return ty == Tstruct || ty == Tsarray; - } + void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) { + // FIXME + } - void rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) - { - for (auto arg : fty.args) - { - if (!arg->byref) - rewriteArgument(fty, *arg); - } - } - - void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg) - { - // FIXME - } - - // Returns true if the D type can be bit-cast to an integer of the same size. - bool canRewriteAsInt(Type* t) - { - const unsigned size = t->size(); - return size == 1 || size == 2 || size == 4 || (Is64Bit && size == 8); - } + // Returns true if the D type can be bit-cast to an integer of the same size. + bool canRewriteAsInt(Type *t) { + const unsigned size = t->size(); + return size == 1 || size == 2 || size == 4 || (Is64Bit && size == 8); + } }; // The public getter for abi.cpp -TargetABI* getMIPS64TargetABI(bool Is64Bit) -{ - return new MIPS64TargetABI(Is64Bit); +TargetABI *getMIPS64TargetABI(bool Is64Bit) { + return new MIPS64TargetABI(Is64Bit); } diff --git a/gen/abi-mips64.h b/gen/abi-mips64.h index 43bffb7e50..19e706ab99 100644 --- a/gen/abi-mips64.h +++ b/gen/abi-mips64.h @@ -16,6 +16,6 @@ struct TargetABI; -TargetABI* getMIPS64TargetABI(bool Is64Bit); +TargetABI *getMIPS64TargetABI(bool Is64Bit); #endif diff --git a/gen/abi-ppc64.cpp b/gen/abi-ppc64.cpp index 4f9b33a509..bae4f81e6c 100644 --- a/gen/abi-ppc64.cpp +++ b/gen/abi-ppc64.cpp @@ -21,82 +21,67 @@ #include "gen/tollvm.h" struct PPC64TargetABI : TargetABI { - ExplicitByvalRewrite byvalRewrite; - IntegerRewrite integerRewrite; - const bool Is64Bit; + ExplicitByvalRewrite byvalRewrite; + IntegerRewrite integerRewrite; + const bool Is64Bit; - PPC64TargetABI(const bool Is64Bit) : Is64Bit(Is64Bit) - { } + PPC64TargetABI(const bool Is64Bit) : Is64Bit(Is64Bit) {} - bool returnInArg(TypeFunction* tf) - { - if (tf->isref) - return false; + bool returnInArg(TypeFunction *tf) { + if (tf->isref) + return false; - // Return structs and static arrays on the stack. The latter is needed - // because otherwise LLVM tries to actually return the array in a number - // of physical registers, which leads, depending on the target, to - // either horrendous codegen or backend crashes. - Type* rt = tf->next->toBasetype(); - return (rt->ty == Tstruct || rt->ty == Tsarray); + // Return structs and static arrays on the stack. The latter is needed + // because otherwise LLVM tries to actually return the array in a number + // of physical registers, which leads, depending on the target, to + // either horrendous codegen or backend crashes. + Type *rt = tf->next->toBasetype(); + return (rt->ty == Tstruct || rt->ty == Tsarray); + } + + bool passByVal(Type *t) { + TY ty = t->toBasetype()->ty; + return ty == Tstruct || ty == Tsarray; + } + + void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) { + // EXPLICIT PARAMETERS + for (auto arg : fty.args) { + if (!arg->byref) + rewriteArgument(fty, *arg); } + } - bool passByVal(Type* t) - { - TY ty = t->toBasetype()->ty; - return ty == Tstruct || ty == Tsarray; - } + void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) { + Type *ty = arg.type->toBasetype(); - void rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) - { - // EXPLICIT PARAMETERS - for (auto arg : fty.args) - { - if (!arg->byref) - rewriteArgument(fty, *arg); + if (ty->ty == Tstruct || ty->ty == Tsarray) { + if (canRewriteAsInt(ty)) { + if (!IntegerRewrite::isObsoleteFor(arg.ltype)) { + arg.rewrite = &integerRewrite; + arg.ltype = integerRewrite.type(arg.type, arg.ltype); } + } else { + // these types are passed byval: + // the caller allocates a copy and then passes a pointer to the copy + arg.rewrite = &byvalRewrite; + arg.ltype = byvalRewrite.type(arg.type, arg.ltype); + + // the copy is treated as a local variable of the callee + // hence add the NoAlias and NoCapture attributes + arg.attrs.clear().add(LLAttribute::NoAlias).add(LLAttribute::NoCapture); + } } + } - void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg) - { - Type* ty = arg.type->toBasetype(); - - if (ty->ty == Tstruct || ty->ty == Tsarray) - { - if (canRewriteAsInt(ty)) - { - if (!IntegerRewrite::isObsoleteFor(arg.ltype)) - { - arg.rewrite = &integerRewrite; - arg.ltype = integerRewrite.type(arg.type, arg.ltype); - } - } - else - { - // these types are passed byval: - // the caller allocates a copy and then passes a pointer to the copy - arg.rewrite = &byvalRewrite; - arg.ltype = byvalRewrite.type(arg.type, arg.ltype); - - // the copy is treated as a local variable of the callee - // hence add the NoAlias and NoCapture attributes - arg.attrs.clear() - .add(LLAttribute::NoAlias) - .add(LLAttribute::NoCapture); - } - } - } - - // Returns true if the D type can be bit-cast to an integer of the same size. - bool canRewriteAsInt(Type* t) - { - const unsigned size = t->size(); - return size == 1 || size == 2 || size == 4 || (Is64Bit && size == 8); - } + // Returns true if the D type can be bit-cast to an integer of the same size. + bool canRewriteAsInt(Type *t) { + const unsigned size = t->size(); + return size == 1 || size == 2 || size == 4 || (Is64Bit && size == 8); + } }; // The public getter for abi.cpp -TargetABI* getPPC64TargetABI(bool Is64Bit) -{ - return new PPC64TargetABI(Is64Bit); +TargetABI *getPPC64TargetABI(bool Is64Bit) { + return new PPC64TargetABI(Is64Bit); } diff --git a/gen/abi-ppc64.h b/gen/abi-ppc64.h index eb8c818a66..f2f602866d 100644 --- a/gen/abi-ppc64.h +++ b/gen/abi-ppc64.h @@ -16,6 +16,6 @@ struct TargetABI; -TargetABI* getPPC64TargetABI(bool Is64Bit); +TargetABI *getPPC64TargetABI(bool Is64Bit); #endif diff --git a/gen/abi-win64.cpp b/gen/abi-win64.cpp index a132c41573..d6bc0acf72 100644 --- a/gen/abi-win64.cpp +++ b/gen/abi-win64.cpp @@ -31,141 +31,119 @@ #include #include -struct Win64TargetABI : TargetABI -{ - ExplicitByvalRewrite byvalRewrite; - IntegerRewrite integerRewrite; +struct Win64TargetABI : TargetABI { + ExplicitByvalRewrite byvalRewrite; + IntegerRewrite integerRewrite; - bool returnInArg(TypeFunction* tf); + bool returnInArg(TypeFunction *tf); - bool passByVal(Type* t); + bool passByVal(Type *t); - bool passThisBeforeSret(TypeFunction* tf); + bool passThisBeforeSret(TypeFunction *tf); - void rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty); + void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty); - void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg); + void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg); private: - // Returns true if the D type is an aggregate: - // * struct - // * static/dynamic array - // * delegate - // * complex number - bool isAggregate(Type* t) - { - TY ty = t->ty; - return ty == Tstruct || ty == Tsarray || /*ty == Tarray ||*/ ty == Tdelegate - || t->iscomplex(); - } + // Returns true if the D type is an aggregate: + // * struct + // * static/dynamic array + // * delegate + // * complex number + bool isAggregate(Type *t) { + TY ty = t->ty; + return ty == Tstruct || ty == Tsarray || + /*ty == Tarray ||*/ ty == Tdelegate || t->iscomplex(); + } - // Returns true if the D type can be bit-cast to an integer of the same size. - bool canRewriteAsInt(Type* t) - { - unsigned size = t->size(); - return size == 1 || size == 2 || size == 4 || size == 8; - } + // Returns true if the D type can be bit-cast to an integer of the same size. + bool canRewriteAsInt(Type *t) { + unsigned size = t->size(); + return size == 1 || size == 2 || size == 4 || size == 8; + } - bool realIs80bits() - { - return !global.params.targetTriple.isWindowsMSVCEnvironment(); - } + bool realIs80bits() { + return !global.params.targetTriple.isWindowsMSVCEnvironment(); + } - // Returns true if the D type is passed byval (the callee getting a pointer - // to a dedicated hidden copy). - bool isPassedWithByvalSemantics(Type* t) - { - return - // * aggregates which can NOT be rewritten as integers - // (size > 64 bits or not a power of 2) - (isAggregate(t) && !canRewriteAsInt(t)) || - // * 80-bit real and ireal - (realIs80bits() && (t->ty == Tfloat80 || t->ty == Timaginary80)); - } + // Returns true if the D type is passed byval (the callee getting a pointer + // to a dedicated hidden copy). + bool isPassedWithByvalSemantics(Type *t) { + return + // * aggregates which can NOT be rewritten as integers + // (size > 64 bits or not a power of 2) + (isAggregate(t) && !canRewriteAsInt(t)) || + // * 80-bit real and ireal + (realIs80bits() && (t->ty == Tfloat80 || t->ty == Timaginary80)); + } }; - // The public getter for abi.cpp -TargetABI* getWin64TargetABI() -{ - return new Win64TargetABI; -} +TargetABI *getWin64TargetABI() { return new Win64TargetABI; } -bool Win64TargetABI::returnInArg(TypeFunction* tf) -{ - if (tf->isref) - return false; - - Type* rt = tf->next->toBasetype(); - - // * let LLVM return 80-bit real/ireal on the x87 stack, for DMD compliance - if (realIs80bits() && (rt->ty == Tfloat80 || rt->ty == Timaginary80)) - return false; - - // * all POD types <= 64 bits and of a size that is a power of 2 - // (incl. 2x32-bit cfloat) are returned in a register (RAX, or - // XMM0 for single float/ifloat/double/idouble) - // * all other types are returned via struct-return (sret) - return (rt->ty == Tstruct && !((TypeStruct*)rt)->sym->isPOD()) - || isPassedWithByvalSemantics(rt); -} - -bool Win64TargetABI::passByVal(Type* t) -{ +bool Win64TargetABI::returnInArg(TypeFunction *tf) { + if (tf->isref) return false; + + Type *rt = tf->next->toBasetype(); + + // * let LLVM return 80-bit real/ireal on the x87 stack, for DMD compliance + if (realIs80bits() && (rt->ty == Tfloat80 || rt->ty == Timaginary80)) + return false; + + // * all POD types <= 64 bits and of a size that is a power of 2 + // (incl. 2x32-bit cfloat) are returned in a register (RAX, or + // XMM0 for single float/ifloat/double/idouble) + // * all other types are returned via struct-return (sret) + return (rt->ty == Tstruct && !((TypeStruct *)rt)->sym->isPOD()) || + isPassedWithByvalSemantics(rt); } -bool Win64TargetABI::passThisBeforeSret(TypeFunction* tf) -{ - return tf->linkage == LINKcpp; +bool Win64TargetABI::passByVal(Type *t) { return false; } + +bool Win64TargetABI::passThisBeforeSret(TypeFunction *tf) { + return tf->linkage == LINKcpp; } -void Win64TargetABI::rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) -{ - // RETURN VALUE - if (!fty.ret->byref && fty.ret->type->toBasetype()->ty != Tvoid) - rewriteArgument(fty, *fty.ret); +void Win64TargetABI::rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) { + // RETURN VALUE + if (!fty.ret->byref && fty.ret->type->toBasetype()->ty != Tvoid) + rewriteArgument(fty, *fty.ret); - // EXPLICIT PARAMETERS - for (auto arg : fty.args) - { - if (!arg->byref) - rewriteArgument(fty, *arg); - } + // EXPLICIT PARAMETERS + for (auto arg : fty.args) { + if (!arg->byref) + rewriteArgument(fty, *arg); + } - // extern(D): reverse parameter order for non variadics, for DMD-compliance - if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) - fty.reverseParams = true; + // extern(D): reverse parameter order for non variadics, for DMD-compliance + if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) + fty.reverseParams = true; } -void Win64TargetABI::rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg) -{ - LLType* originalLType = arg.ltype; - Type* t = arg.type->toBasetype(); +void Win64TargetABI::rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) { + LLType *originalLType = arg.ltype; + Type *t = arg.type->toBasetype(); - if (isPassedWithByvalSemantics(t)) - { - // these types are passed byval: - // the caller allocates a copy and then passes a pointer to the copy - arg.rewrite = &byvalRewrite; - arg.ltype = byvalRewrite.type(arg.type, arg.ltype); + if (isPassedWithByvalSemantics(t)) { + // these types are passed byval: + // the caller allocates a copy and then passes a pointer to the copy + arg.rewrite = &byvalRewrite; + arg.ltype = byvalRewrite.type(arg.type, arg.ltype); - // the copy is treated as a local variable of the callee - // hence add the NoAlias and NoCapture attributes - arg.attrs.clear() - .add(LLAttribute::NoAlias) - .add(LLAttribute::NoCapture); - } - else if (isAggregate(t) && canRewriteAsInt(t) && !IntegerRewrite::isObsoleteFor(originalLType)) - { - arg.rewrite = &integerRewrite; - arg.ltype = integerRewrite.type(arg.type, arg.ltype); - } + // the copy is treated as a local variable of the callee + // hence add the NoAlias and NoCapture attributes + arg.attrs.clear().add(LLAttribute::NoAlias).add(LLAttribute::NoCapture); + } else if (isAggregate(t) && canRewriteAsInt(t) && + !IntegerRewrite::isObsoleteFor(originalLType)) { + arg.rewrite = &integerRewrite; + arg.ltype = integerRewrite.type(arg.type, arg.ltype); + } - IF_LOG if (arg.rewrite) - { - Logger::println("Rewriting argument type %s", t->toChars()); - LOG_SCOPE; - Logger::cout() << *originalLType << " => " << *arg.ltype << '\n'; - } + IF_LOG if (arg.rewrite) { + Logger::println("Rewriting argument type %s", t->toChars()); + LOG_SCOPE; + Logger::cout() << *originalLType << " => " << *arg.ltype << '\n'; + } } diff --git a/gen/abi-win64.h b/gen/abi-win64.h index 580c1c1843..ea551e9886 100644 --- a/gen/abi-win64.h +++ b/gen/abi-win64.h @@ -17,8 +17,6 @@ #include "gen/abi.h" - -TargetABI* getWin64TargetABI(); - +TargetABI *getWin64TargetABI(); #endif diff --git a/gen/abi-x86-64.cpp b/gen/abi-x86-64.cpp index 32917aa46f..116bd8a51c 100644 --- a/gen/abi-x86-64.cpp +++ b/gen/abi-x86-64.cpp @@ -44,115 +44,116 @@ #include #include -TypeTuple* toArgTypes(Type* t); // in dmd2/argtypes.c +TypeTuple *toArgTypes(Type *t); // in dmd2/argtypes.c namespace { - namespace dmd_abi { - // Structs, static arrays and cfloats may be rewritten to exploit registers. - // This function returns the rewritten type, or null if no transformation is needed. - LLType* getAbiType(Type* ty) { - // First, check if there's any need of a transformation: - if (!(ty->ty == Tcomplex32 || ty->ty == Tstruct || ty->ty == Tsarray)) - return NULL; // Nothing to do +namespace dmd_abi { +// Structs, static arrays and cfloats may be rewritten to exploit registers. +// This function returns the rewritten type, or null if no transformation is +// needed. +LLType *getAbiType(Type *ty) { + // First, check if there's any need of a transformation: + if (!(ty->ty == Tcomplex32 || ty->ty == Tstruct || ty->ty == Tsarray)) + return NULL; // Nothing to do - // Okay, we may need to transform. Figure out a canonical type: + // Okay, we may need to transform. Figure out a canonical type: - TypeTuple* argTypes = toArgTypes(ty); - if (!argTypes || argTypes->arguments->empty()) - return NULL; // don't rewrite + TypeTuple *argTypes = toArgTypes(ty); + if (!argTypes || argTypes->arguments->empty()) + return NULL; // don't rewrite - LLType* abiTy = NULL; - if (argTypes->arguments->size() == 1) { - abiTy = DtoType((*argTypes->arguments->begin())->type); - // don't rewrite to a single bit (assertions in tollvm.cpp), choose a byte instead - abiTy = i1ToI8(abiTy); - } else { - std::vector parts; - for (auto param : *argTypes->arguments) { - LLType* partType = DtoType(param->type); - // round up the DMD argtype for an eightbyte of a struct to a corresponding 64-bit type - // this makes sure that 64 bits of the chosen register are used and thus - // makes sure all potential padding bytes of a struct are copied - if (partType->isIntegerTy()) - partType = LLType::getInt64Ty(gIR->context()); - else if (partType->isFloatTy()) - partType = LLType::getDoubleTy(gIR->context()); - parts.push_back(partType); - } - abiTy = LLStructType::get(gIR->context(), parts); - } - - return abiTy; + LLType *abiTy = NULL; + if (argTypes->arguments->size() == 1) { + abiTy = DtoType((*argTypes->arguments->begin())->type); + // don't rewrite to a single bit (assertions in tollvm.cpp), choose a byte + // instead + abiTy = i1ToI8(abiTy); + } else { + std::vector parts; + for (auto param : *argTypes->arguments) { + LLType *partType = DtoType(param->type); + // round up the DMD argtype for an eightbyte of a struct to a + // corresponding 64-bit type + // this makes sure that 64 bits of the chosen register are used and thus + // makes sure all potential padding bytes of a struct are copied + if (partType->isIntegerTy()) + partType = LLType::getInt64Ty(gIR->context()); + else if (partType->isFloatTy()) + partType = LLType::getDoubleTy(gIR->context()); + parts.push_back(partType); } + abiTy = LLStructType::get(gIR->context(), parts); + } - bool passByVal(Type* ty) { - TypeTuple* argTypes = toArgTypes(ty); - if (!argTypes) - return false; + return abiTy; +} - return argTypes->arguments->empty(); // empty => cannot be passed in registers +bool passByVal(Type *ty) { + TypeTuple *argTypes = toArgTypes(ty); + if (!argTypes) + return false; + + return argTypes->arguments->empty(); // empty => cannot be passed in registers +} +} // namespace dmd_abi + +LLType *getAbiType(Type *ty) { return dmd_abi::getAbiType(ty->toBasetype()); } + +struct RegCount { + char int_regs, sse_regs; + + RegCount() : int_regs(6), sse_regs(8) {} + + explicit RegCount(LLType *ty) : int_regs(0), sse_regs(0) { + if (LLStructType *structTy = isaStruct(ty)) { + for (unsigned i = 0; i < structTy->getNumElements(); ++i) { + RegCount elementRegCount(structTy->getElementType(i)); + int_regs += elementRegCount.int_regs; + sse_regs += elementRegCount.sse_regs; + } + + assert(int_regs + sse_regs <= 2); + } else { // not a struct + if (ty->isIntegerTy() || ty->isPointerTy()) { + ++int_regs; + } else if (ty->isFloatingPointTy() || ty->isVectorTy()) { + // X87 reals are passed on the stack + if (!ty->isX86_FP80Ty()) + ++sse_regs; + } else { + unsigned sizeInBits = gDataLayout->getTypeSizeInBits(ty); + IF_LOG Logger::cout() + << "SysV RegCount: assuming 1 GP register for type " << *ty << " (" + << sizeInBits << " bits)\n"; + assert(sizeInBits > 0 && sizeInBits <= 64); + ++int_regs; + } } - } // namespace dmd_abi + } - LLType* getAbiType(Type* ty) { - return dmd_abi::getAbiType(ty->toBasetype()); - } + enum SubtractionResult { + ArgumentFitsIn, + ArgumentWouldFitInPartially, + ArgumentDoesntFitIn + }; - struct RegCount { - char int_regs, sse_regs; + SubtractionResult trySubtract(const IrFuncTyArg &arg) { + const RegCount wanted(arg.ltype); - RegCount() : int_regs(6), sse_regs(8) {} + const bool anyRegAvailable = (wanted.int_regs > 0 && int_regs > 0) || + (wanted.sse_regs > 0 && sse_regs > 0); + if (!anyRegAvailable) + return ArgumentDoesntFitIn; - explicit RegCount(LLType* ty) : int_regs(0), sse_regs(0) { - if (LLStructType* structTy = isaStruct(ty)) { - for (unsigned i = 0; i < structTy->getNumElements(); ++i) - { - RegCount elementRegCount(structTy->getElementType(i)); - int_regs += elementRegCount.int_regs; - sse_regs += elementRegCount.sse_regs; - } + if (int_regs < wanted.int_regs || sse_regs < wanted.sse_regs) + return ArgumentWouldFitInPartially; - assert(int_regs + sse_regs <= 2); - } else { // not a struct - if (ty->isIntegerTy() || ty->isPointerTy()) { - ++int_regs; - } else if (ty->isFloatingPointTy() || ty->isVectorTy()) { - // X87 reals are passed on the stack - if (!ty->isX86_FP80Ty()) - ++sse_regs; - } else { - unsigned sizeInBits = gDataLayout->getTypeSizeInBits(ty); - IF_LOG Logger::cout() << "SysV RegCount: assuming 1 GP register for type " << *ty - << " (" << sizeInBits << " bits)\n"; - assert(sizeInBits > 0 && sizeInBits <= 64); - ++int_regs; - } - } - } + int_regs -= wanted.int_regs; + sse_regs -= wanted.sse_regs; - enum SubtractionResult { - ArgumentFitsIn, - ArgumentWouldFitInPartially, - ArgumentDoesntFitIn - }; - - SubtractionResult trySubtract(const IrFuncTyArg& arg) { - const RegCount wanted(arg.ltype); - - const bool anyRegAvailable = (wanted.int_regs > 0 && int_regs > 0) || - (wanted.sse_regs > 0 && sse_regs > 0); - if (!anyRegAvailable) - return ArgumentDoesntFitIn; - - if (int_regs < wanted.int_regs || sse_regs < wanted.sse_regs) - return ArgumentWouldFitInPartially; - - int_regs -= wanted.int_regs; - sse_regs -= wanted.sse_regs; - - return ArgumentFitsIn; - } - }; + return ArgumentFitsIn; + } +}; } /** @@ -160,29 +161,24 @@ namespace { * memory so that it's then readable as the other type (i.e., bit-casting). */ struct X86_64_C_struct_rewrite : ABIRewrite { - LLValue* get(Type* dty, LLValue* v) - { - LLValue* address = DtoAllocaDump(v, dty, ".X86_64_C_struct_rewrite_dump"); - LLType* type = DtoType(dty); - return loadFromMemory(address, type, ".X86_64_C_struct_rewrite_getResult"); - } + LLValue *get(Type *dty, LLValue *v) { + LLValue *address = DtoAllocaDump(v, dty, ".X86_64_C_struct_rewrite_dump"); + LLType *type = DtoType(dty); + return loadFromMemory(address, type, ".X86_64_C_struct_rewrite_getResult"); + } - void getL(Type* dty, LLValue* v, LLValue* lval) { - storeToMemory(v, lval); - } + void getL(Type *dty, LLValue *v, LLValue *lval) { storeToMemory(v, lval); } - LLValue* put(DValue* v) { - LLValue* address = getAddressOf(v); + LLValue *put(DValue *v) { + LLValue *address = getAddressOf(v); - LLType* abiTy = getAbiType(v->getType()); - assert(abiTy && "Why are we rewriting a non-rewritten type?"); + LLType *abiTy = getAbiType(v->getType()); + assert(abiTy && "Why are we rewriting a non-rewritten type?"); - return loadFromMemory(address, abiTy, ".X86_64_C_struct_rewrite_putResult"); - } + return loadFromMemory(address, abiTy, ".X86_64_C_struct_rewrite_putResult"); + } - LLType* type(Type* dty, LLType* t) { - return getAbiType(dty); - } + LLType *type(Type *dty, LLType *t) { return getAbiType(dty); } }; /** @@ -194,216 +190,218 @@ struct X86_64_C_struct_rewrite : ABIRewrite { * the ByVal LLVM attribute. */ struct ImplicitByvalRewrite : ABIRewrite { - LLValue* get(Type* dty, LLValue* v) { - return DtoLoad(v, ".ImplicitByvalRewrite_getResult"); - } + LLValue *get(Type *dty, LLValue *v) { + return DtoLoad(v, ".ImplicitByvalRewrite_getResult"); + } - void getL(Type* dty, LLValue* v, LLValue* lval) { - DtoAggrCopy(lval, v); - } + void getL(Type *dty, LLValue *v, LLValue *lval) { DtoAggrCopy(lval, v); } - LLValue* put(DValue* v) { - return getAddressOf(v); - } + LLValue *put(DValue *v) { return getAddressOf(v); } - LLType* type(Type* dty, LLType* t) { - return DtoPtrToType(dty); - } + LLType *type(Type *dty, LLType *t) { return DtoPtrToType(dty); } }; struct X86_64TargetABI : TargetABI { - X86_64_C_struct_rewrite struct_rewrite; - ImplicitByvalRewrite byvalRewrite; + X86_64_C_struct_rewrite struct_rewrite; + ImplicitByvalRewrite byvalRewrite; - bool returnInArg(TypeFunction* tf); + bool returnInArg(TypeFunction *tf); - bool passByVal(Type* t); + bool passByVal(Type *t); - void rewriteFunctionType(TypeFunction* tf, IrFuncTy& fty); - void rewriteVarargs(IrFuncTy& fty, std::vector& args); - void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg); - void rewriteArgument(IrFuncTyArg& arg, RegCount& regCount); + void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty); + void rewriteVarargs(IrFuncTy &fty, std::vector &args); + void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg); + void rewriteArgument(IrFuncTyArg &arg, RegCount ®Count); - LLValue* prepareVaStart(LLValue* pAp); + LLValue *prepareVaStart(LLValue *pAp); - void vaCopy(LLValue* pDest, LLValue* src); + void vaCopy(LLValue *pDest, LLValue *src); - LLValue* prepareVaArg(LLValue* pAp); + LLValue *prepareVaArg(LLValue *pAp); - Type *vaListType(); + Type *vaListType(); private: - LLType* getValistType(); - RegCount& getRegCount(IrFuncTy& fty) { return reinterpret_cast(fty.tag); } + LLType *getValistType(); + RegCount &getRegCount(IrFuncTy &fty) { + return reinterpret_cast(fty.tag); + } }; - // The public getter for abi.cpp -TargetABI* getX86_64TargetABI() { - return new X86_64TargetABI; +TargetABI *getX86_64TargetABI() { return new X86_64TargetABI; } + +bool X86_64TargetABI::returnInArg(TypeFunction *tf) { + if (tf->isref) + return false; + + Type *rt = tf->next; + return passByVal(rt); } -bool X86_64TargetABI::returnInArg(TypeFunction* tf) { - if (tf->isref) - return false; - - Type* rt = tf->next; - return passByVal(rt); +bool X86_64TargetABI::passByVal(Type *t) { + return dmd_abi::passByVal(t->toBasetype()); } -bool X86_64TargetABI::passByVal(Type* t) { - return dmd_abi::passByVal(t->toBasetype()); +void X86_64TargetABI::rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) { + llvm_unreachable("Please use the other overload explicitly."); } -void X86_64TargetABI::rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg) { - llvm_unreachable("Please use the other overload explicitly."); -} +void X86_64TargetABI::rewriteArgument(IrFuncTyArg &arg, RegCount ®Count) { + LLType *originalLType = arg.ltype; + Type *t = arg.type->toBasetype(); -void X86_64TargetABI::rewriteArgument(IrFuncTyArg& arg, RegCount& regCount) { - LLType* originalLType = arg.ltype; - Type* t = arg.type->toBasetype(); - - LLType* abiTy = getAbiType(t); - if (abiTy && !LLTypeMemoryLayout::typesAreEquivalent(abiTy, originalLType)) { - IF_LOG { - Logger::println("Rewriting argument type %s", t->toChars()); - LOG_SCOPE; - Logger::cout() << *originalLType << " => " << *abiTy << '\n'; - } - - arg.rewrite = &struct_rewrite; - arg.ltype = abiTy; + LLType *abiTy = getAbiType(t); + if (abiTy && !LLTypeMemoryLayout::typesAreEquivalent(abiTy, originalLType)) { + IF_LOG { + Logger::println("Rewriting argument type %s", t->toChars()); + LOG_SCOPE; + Logger::cout() << *originalLType << " => " << *abiTy << '\n'; } - if (regCount.trySubtract(arg) == RegCount::ArgumentWouldFitInPartially) { - // pass LL structs implicitly ByVal, otherwise LLVM passes - // them partially in registers, partially in memory - assert(originalLType->isStructTy()); - IF_LOG Logger::cout() << "Passing implicitly ByVal: " << arg.type->toChars() << " (" << *originalLType << ")\n"; - arg.rewrite = &byvalRewrite; - arg.ltype = originalLType->getPointerTo(); - arg.attrs.add(LLAttribute::ByVal); - } + arg.rewrite = &struct_rewrite; + arg.ltype = abiTy; + } + + if (regCount.trySubtract(arg) == RegCount::ArgumentWouldFitInPartially) { + // pass LL structs implicitly ByVal, otherwise LLVM passes + // them partially in registers, partially in memory + assert(originalLType->isStructTy()); + IF_LOG Logger::cout() << "Passing implicitly ByVal: " << arg.type->toChars() + << " (" << *originalLType << ")\n"; + arg.rewrite = &byvalRewrite; + arg.ltype = originalLType->getPointerTo(); + arg.attrs.add(LLAttribute::ByVal); + } } -void X86_64TargetABI::rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) { - RegCount& regCount = getRegCount(fty); - regCount = RegCount(); // initialize +void X86_64TargetABI::rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) { + RegCount ®Count = getRegCount(fty); + regCount = RegCount(); // initialize - // RETURN VALUE - if (!fty.ret->byref && fty.ret->type->toBasetype()->ty != Tvoid) { - Logger::println("x86-64 ABI: Transforming return type"); - LOG_SCOPE; - RegCount dummy; - rewriteArgument(*fty.ret, dummy); - } - - // IMPLICIT PARAMETERS - if (fty.arg_sret) - regCount.int_regs--; - if (fty.arg_this || fty.arg_nest) - regCount.int_regs--; - if (fty.arg_arguments) - regCount.int_regs -= 2; // dynamic array - - // EXPLICIT PARAMETERS - Logger::println("x86-64 ABI: Transforming argument types"); + // RETURN VALUE + if (!fty.ret->byref && fty.ret->type->toBasetype()->ty != Tvoid) { + Logger::println("x86-64 ABI: Transforming return type"); LOG_SCOPE; + RegCount dummy; + rewriteArgument(*fty.ret, dummy); + } - // extern(D): reverse parameter order for non variadics, for DMD-compliance - if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) - fty.reverseParams = true; + // IMPLICIT PARAMETERS + if (fty.arg_sret) + regCount.int_regs--; + if (fty.arg_this || fty.arg_nest) + regCount.int_regs--; + if (fty.arg_arguments) + regCount.int_regs -= 2; // dynamic array - int begin = 0, end = fty.args.size(), step = 1; - if (fty.reverseParams) { - begin = end - 1; - end = -1; - step = -1; - } - for (int i = begin; i != end; i += step) { - IrFuncTyArg& arg = *fty.args[i]; + // EXPLICIT PARAMETERS + Logger::println("x86-64 ABI: Transforming argument types"); + LOG_SCOPE; - if (arg.byref) { - if (!arg.isByVal() && regCount.int_regs > 0) - regCount.int_regs--; + // extern(D): reverse parameter order for non variadics, for DMD-compliance + if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) + fty.reverseParams = true; - continue; - } + int begin = 0, end = fty.args.size(), step = 1; + if (fty.reverseParams) { + begin = end - 1; + end = -1; + step = -1; + } + for (int i = begin; i != end; i += step) { + IrFuncTyArg &arg = *fty.args[i]; - rewriteArgument(arg, regCount); + if (arg.byref) { + if (!arg.isByVal() && regCount.int_regs > 0) + regCount.int_regs--; + + continue; } - // regCount (fty.tag) is now in the state after all implicit & formal args, - // ready to serve as initial state for each vararg call site, see below + rewriteArgument(arg, regCount); + } + + // regCount (fty.tag) is now in the state after all implicit & formal args, + // ready to serve as initial state for each vararg call site, see below } -void X86_64TargetABI::rewriteVarargs(IrFuncTy& fty, std::vector& args) -{ - // use a dedicated RegCount copy for each call site and initialize it with fty.tag - RegCount regCount = getRegCount(fty); +void X86_64TargetABI::rewriteVarargs(IrFuncTy &fty, + std::vector &args) { + // use a dedicated RegCount copy for each call site and initialize it with + // fty.tag + RegCount regCount = getRegCount(fty); - for (unsigned i = 0; i < args.size(); ++i) - { - IrFuncTyArg& arg = *args[i]; - if (!arg.byref) // don't rewrite ByVal arguments - rewriteArgument(arg, regCount); - } + for (unsigned i = 0; i < args.size(); ++i) { + IrFuncTyArg &arg = *args[i]; + if (!arg.byref) // don't rewrite ByVal arguments + rewriteArgument(arg, regCount); + } } - /** - * The System V AMD64 ABI uses a special native va_list type - a 24-bytes struct passed by + * The System V AMD64 ABI uses a special native va_list type - a 24-bytes struct + * passed by * reference. - * In druntime, the struct is defined as core.stdc.stdarg.__va_list; the actually used - * core.stdc.stdarg.va_list type is a raw char* pointer though to achieve byref semantics. - * This requires a little bit of compiler magic in the following implementations. + * In druntime, the struct is defined as core.stdc.stdarg.__va_list; the + * actually used + * core.stdc.stdarg.va_list type is a raw char* pointer though to achieve byref + * semantics. + * This requires a little bit of compiler magic in the following + * implementations. */ -LLType* X86_64TargetABI::getValistType() { - LLType* uintType = LLType::getInt32Ty(gIR->context()); - LLType* voidPointerType = getVoidPtrType(); +LLType *X86_64TargetABI::getValistType() { + LLType *uintType = LLType::getInt32Ty(gIR->context()); + LLType *voidPointerType = getVoidPtrType(); - std::vector parts; // struct __va_list { - parts.push_back(uintType); // uint gp_offset; - parts.push_back(uintType); // uint fp_offset; - parts.push_back(voidPointerType); // void* overflow_arg_area; - parts.push_back(voidPointerType); // void* reg_save_area; } + std::vector parts; // struct __va_list { + parts.push_back(uintType); // uint gp_offset; + parts.push_back(uintType); // uint fp_offset; + parts.push_back(voidPointerType); // void* overflow_arg_area; + parts.push_back(voidPointerType); // void* reg_save_area; } - return LLStructType::get(gIR->context(), parts); + return LLStructType::get(gIR->context(), parts); } -LLValue* X86_64TargetABI::prepareVaStart(LLValue* pAp) { - // Since the user only created a char* pointer (ap) on the stack before invoking va_start, - // we first need to allocate the actual __va_list struct and set 'ap' to its address. - LLValue* valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem"); - valistmem = DtoBitCast(valistmem, getVoidPtrType()); - DtoStore(valistmem, DtoBitCast(pAp, getPtrToType(getVoidPtrType()))); +LLValue *X86_64TargetABI::prepareVaStart(LLValue *pAp) { + // Since the user only created a char* pointer (ap) on the stack before + // invoking va_start, + // we first need to allocate the actual __va_list struct and set 'ap' to its + // address. + LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem"); + valistmem = DtoBitCast(valistmem, getVoidPtrType()); + DtoStore(valistmem, DtoBitCast(pAp, getPtrToType(getVoidPtrType()))); - // pass a void* pointer to the actual struct to LLVM's va_start intrinsic - return valistmem; + // pass a void* pointer to the actual struct to LLVM's va_start intrinsic + return valistmem; } -void X86_64TargetABI::vaCopy(LLValue* pDest, LLValue* src) { - // Analog to va_start, we need to allocate a new __va_list struct on the stack, - // fill it with a bitcopy of the source struct... - src = DtoLoad(DtoBitCast(src, getValistType()->getPointerTo())); // *(__va_list*)src - LLValue* valistmem = DtoAllocaDump(src, 0, "__va_list_mem"); - // ... and finally set the passed 'dest' char* pointer to the new struct's address. - DtoStore(DtoBitCast(valistmem, getVoidPtrType()), - DtoBitCast(pDest, getPtrToType(getVoidPtrType()))); +void X86_64TargetABI::vaCopy(LLValue *pDest, LLValue *src) { + // Analog to va_start, we need to allocate a new __va_list struct on the + // stack, + // fill it with a bitcopy of the source struct... + src = DtoLoad( + DtoBitCast(src, getValistType()->getPointerTo())); // *(__va_list*)src + LLValue *valistmem = DtoAllocaDump(src, 0, "__va_list_mem"); + // ... and finally set the passed 'dest' char* pointer to the new struct's + // address. + DtoStore(DtoBitCast(valistmem, getVoidPtrType()), + DtoBitCast(pDest, getPtrToType(getVoidPtrType()))); } -LLValue* X86_64TargetABI::prepareVaArg(LLValue* pAp) -{ - // pass a void* pointer to the actual __va_list struct to LLVM's va_arg intrinsic - return DtoLoad(pAp); +LLValue *X86_64TargetABI::prepareVaArg(LLValue *pAp) { + // pass a void* pointer to the actual __va_list struct to LLVM's va_arg + // intrinsic + return DtoLoad(pAp); } -Type* X86_64TargetABI::vaListType() { - // We need to pass the actual va_list type for correct mangling. Simply - // using TypeIdentifier here is a bit wonky but works, as long as the name - // is actually available in the scope (this is what DMD does, so if a better - // solution is found there, this should be adapted). - return (new TypeIdentifier(Loc(), - Identifier::idPool("__va_list_tag")))->pointerTo(); +Type *X86_64TargetABI::vaListType() { + // We need to pass the actual va_list type for correct mangling. Simply + // using TypeIdentifier here is a bit wonky but works, as long as the name + // is actually available in the scope (this is what DMD does, so if a better + // solution is found there, this should be adapted). + return (new TypeIdentifier(Loc(), Identifier::idPool("__va_list_tag"))) + ->pointerTo(); } diff --git a/gen/abi-x86-64.h b/gen/abi-x86-64.h index 4add80bd87..2694c05895 100644 --- a/gen/abi-x86-64.h +++ b/gen/abi-x86-64.h @@ -16,8 +16,10 @@ #define LDC_GEN_ABI_X86_64_H struct TargetABI; -namespace llvm { class Type; } +namespace llvm { +class Type; +} -TargetABI* getX86_64TargetABI(); +TargetABI *getX86_64TargetABI(); #endif diff --git a/gen/abi-x86.cpp b/gen/abi-x86.cpp index 2997c941d8..66295c742c 100644 --- a/gen/abi-x86.cpp +++ b/gen/abi-x86.cpp @@ -20,262 +20,226 @@ #include "ir/irfunction.h" #include "ir/irfuncty.h" +struct X86TargetABI : TargetABI { + const bool isOSX; + IntegerRewrite integerRewrite; -struct X86TargetABI : TargetABI -{ - const bool isOSX; - IntegerRewrite integerRewrite; + X86TargetABI() : isOSX(global.params.isOSX) {} - X86TargetABI() - : isOSX(global.params.isOSX) - { + llvm::CallingConv::ID callingConv(llvm::FunctionType *ft, LINK l) { + switch (l) { + case LINKc: + case LINKcpp: + return llvm::CallingConv::C; + case LINKd: + case LINKdefault: + case LINKpascal: + case LINKwindows: + return ft->isVarArg() ? llvm::CallingConv::C + : llvm::CallingConv::X86_StdCall; + default: + llvm_unreachable("Unhandled D linkage type."); } + } - llvm::CallingConv::ID callingConv(llvm::FunctionType* ft, LINK l) - { - switch (l) - { - case LINKc: - case LINKcpp: - return llvm::CallingConv::C; - case LINKd: - case LINKdefault: - case LINKpascal: - case LINKwindows: - return ft->isVarArg() ? llvm::CallingConv::C : llvm::CallingConv::X86_StdCall; - default: - llvm_unreachable("Unhandled D linkage type."); - } - } - - std::string mangleForLLVM(llvm::StringRef name, LINK l) - { - switch (l) - { - case LINKc: - case LINKcpp: - case LINKpascal: - case LINKwindows: - return name; - case LINKd: - case LINKdefault: - if (global.params.targetTriple.isOSWindows()) - { - // Prepend a 0x1 byte to keep LLVM from adding the usual - // "@" stdcall suffix. - return ("\1_" + name).str(); - } - return name; - default: - llvm_unreachable("Unhandled D linkage type."); - } + std::string mangleForLLVM(llvm::StringRef name, LINK l) { + switch (l) { + case LINKc: + case LINKcpp: + case LINKpascal: + case LINKwindows: + return name; + case LINKd: + case LINKdefault: + if (global.params.targetTriple.isOSWindows()) { + // Prepend a 0x1 byte to keep LLVM from adding the usual + // "@" stdcall suffix. + return ("\1_" + name).str(); + } + return name; + default: + llvm_unreachable("Unhandled D linkage type."); } + } private: - bool returnOSXStructInArg(TypeStruct* t) - { - // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/Mac_OS_X_ABI_Function_Calls.pdf - // - // OS X variation on IA-32 for returning structs, page 57 section on - // Returning Results: - // "Structures 1 or 2 bytes in size are placed in EAX. Structures 4 - // or 8 bytes in size are placed in: EAX and EDX. Structures of - // other sizes are placed at the address supplied by the caller." - // Non-POD structs (non-C compatible) should always be returned in an - // arg though (yes, sometimes extern(C) functions return these, but C - // code does not handle struct lifecycle). - size_t sz = t->Type::size(); - return !t->sym->isPOD() || (sz != 1 && sz != 2 && sz != 4 && sz != 8); - } + bool returnOSXStructInArg(TypeStruct *t) { + // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/Mac_OS_X_ABI_Function_Calls.pdf + // + // OS X variation on IA-32 for returning structs, page 57 section on + // Returning Results: + // "Structures 1 or 2 bytes in size are placed in EAX. Structures 4 + // or 8 bytes in size are placed in: EAX and EDX. Structures of + // other sizes are placed at the address supplied by the caller." + // Non-POD structs (non-C compatible) should always be returned in an + // arg though (yes, sometimes extern(C) functions return these, but C + // code does not handle struct lifecycle). + size_t sz = t->Type::size(); + return !t->sym->isPOD() || (sz != 1 && sz != 2 && sz != 4 && sz != 8); + } - bool isMagicCLong(Type *t) - { - // The frontend has magic structs to express the variable-sized C types - // for C++ mangling purposes. We need to pass them like integers, not - // on the stack. + bool isMagicCLong(Type *t) { + // The frontend has magic structs to express the variable-sized C types + // for C++ mangling purposes. We need to pass them like integers, not + // on the stack. - Type * const bt = t->toBasetype(); - if (bt->ty != Tstruct) return false; + Type *const bt = t->toBasetype(); + if (bt->ty != Tstruct) + return false; - Identifier *id = static_cast(bt)->sym->ident; - return (id == Id::__c_long) || (id == Id::__c_ulong); - } + Identifier *id = static_cast(bt)->sym->ident; + return (id == Id::__c_long) || (id == Id::__c_ulong); + } public: - bool returnInArg(TypeFunction* tf) - { - if (tf->isref) - return false; + bool returnInArg(TypeFunction *tf) { + if (tf->isref) + return false; - Type* rt = tf->next->toBasetype(); - // D only returns structs on the stack - if (tf->linkage == LINKd) - { - return rt->ty == Tstruct - || rt->ty == Tsarray - ; - } - // other ABI's follow C, which is cdouble and creal returned on the stack - // as well as structs (except for some OSX cases). - else - { - if (isMagicCLong(rt)) return false; + Type *rt = tf->next->toBasetype(); + // D only returns structs on the stack + if (tf->linkage == LINKd) { + return rt->ty == Tstruct || rt->ty == Tsarray; + } + // other ABI's follow C, which is cdouble and creal returned on the stack + // as well as structs (except for some OSX cases). + else { + if (isMagicCLong(rt)) + return false; - if (rt->ty == Tstruct) - { - return !isOSX || returnOSXStructInArg((TypeStruct*)rt); - } - return (rt->ty == Tsarray || rt->ty == Tcomplex64 || rt->ty == Tcomplex80); + if (rt->ty == Tstruct) { + return !isOSX || returnOSXStructInArg((TypeStruct *)rt); + } + return (rt->ty == Tsarray || rt->ty == Tcomplex64 || + rt->ty == Tcomplex80); + } + } + + bool passByVal(Type *t) { + return t->toBasetype()->ty == Tstruct || t->toBasetype()->ty == Tsarray; + } + + void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) { + // extern(D) + if (tf->linkage == LINKd) { + // IMPLICIT PARAMETERS + + // mark this/nested params inreg + if (fty.arg_this) { + Logger::println("Putting 'this' in register"); + fty.arg_this->attrs.clear().add(LLAttribute::InReg); + } else if (fty.arg_nest) { + Logger::println("Putting context ptr in register"); + fty.arg_nest->attrs.clear().add(LLAttribute::InReg); + } else if (IrFuncTyArg *sret = fty.arg_sret) { + Logger::println("Putting sret ptr in register"); + // sret and inreg are incompatible, but the ABI requires the + // sret parameter to be in EAX in this situation... + sret->attrs.add(LLAttribute::InReg).remove(LLAttribute::StructRet); + } + // otherwise try to mark the last param inreg + else if (!fty.args.empty()) { + // The last parameter is passed in EAX rather than being pushed on the + // stack if the following conditions are met: + // * It fits in EAX. + // * It is not a 3 byte struct. + // * It is not a floating point type. + + IrFuncTyArg *last = fty.args.back(); + Type *lastTy = last->type->toBasetype(); + unsigned sz = lastTy->size(); + + if (last->byref && !last->isByVal()) { + Logger::println("Putting last (byref) parameter in register"); + last->attrs.add(LLAttribute::InReg); + } else if (!lastTy->isfloating() && + (sz == 1 || sz == 2 || sz == 4)) // right? + { + // rewrite the struct into an integer to make inreg work + if (lastTy->ty == Tstruct || lastTy->ty == Tsarray) { + last->rewrite = &integerRewrite; + last->ltype = integerRewrite.type(last->type, last->ltype); + last->byref = false; + // erase previous attributes + last->attrs.clear(); + } + last->attrs.add(LLAttribute::InReg); } + } + + // FIXME: tf->varargs == 1 need to use C calling convention and vararg + // mechanism to live up to the spec: + // "The caller is expected to clean the stack. _argptr is not passed, it + // is computed by the callee." + + // EXPLICIT PARAMETERS + + // reverse parameter order + // for non variadics + if (!fty.args.empty() && tf->varargs != 1) { + fty.reverseParams = true; + } } - bool passByVal(Type* t) - { - return t->toBasetype()->ty == Tstruct || t->toBasetype()->ty == Tsarray; - } + // extern(C) and all others + else { + // RETURN VALUE - void rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) - { - // extern(D) - if (tf->linkage == LINKd) - { - // IMPLICIT PARAMETERS - - // mark this/nested params inreg - if (fty.arg_this) - { - Logger::println("Putting 'this' in register"); - fty.arg_this->attrs.clear().add(LLAttribute::InReg); - } - else if (fty.arg_nest) - { - Logger::println("Putting context ptr in register"); - fty.arg_nest->attrs.clear().add(LLAttribute::InReg); - } - else if (IrFuncTyArg* sret = fty.arg_sret) - { - Logger::println("Putting sret ptr in register"); - // sret and inreg are incompatible, but the ABI requires the - // sret parameter to be in EAX in this situation... - sret->attrs.add(LLAttribute::InReg).remove(LLAttribute::StructRet); - } - // otherwise try to mark the last param inreg - else if (!fty.args.empty()) - { - // The last parameter is passed in EAX rather than being pushed on the stack if the following conditions are met: - // * It fits in EAX. - // * It is not a 3 byte struct. - // * It is not a floating point type. - - IrFuncTyArg* last = fty.args.back(); - Type* lastTy = last->type->toBasetype(); - unsigned sz = lastTy->size(); - - if (last->byref && !last->isByVal()) - { - Logger::println("Putting last (byref) parameter in register"); - last->attrs.add(LLAttribute::InReg); - } - else if (!lastTy->isfloating() && (sz == 1 || sz == 2 || sz == 4)) // right? - { - // rewrite the struct into an integer to make inreg work - if (lastTy->ty == Tstruct || lastTy->ty == Tsarray) - { - last->rewrite = &integerRewrite; - last->ltype = integerRewrite.type(last->type, last->ltype); - last->byref = false; - // erase previous attributes - last->attrs.clear(); - } - last->attrs.add(LLAttribute::InReg); - } - } - - // FIXME: tf->varargs == 1 need to use C calling convention and vararg mechanism to live up to the spec: - // "The caller is expected to clean the stack. _argptr is not passed, it is computed by the callee." - - // EXPLICIT PARAMETERS - - // reverse parameter order - // for non variadics - if (!fty.args.empty() && tf->varargs != 1) - { - fty.reverseParams = true; - } + if ((!fty.ret->byref && isMagicCLong(tf->next)) || + tf->next->toBasetype() == Type::tcomplex32) { + // __c_long -> i32, cfloat -> i64 + fty.ret->rewrite = &integerRewrite; + fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); + } else if (isOSX) { + // value struct returns should be rewritten as an int type to + // generate correct register usage (matches clang). + // note: sret functions change ret type to void so this won't + // trigger for those + Type *retTy = fty.ret->type->toBasetype(); + if (!fty.ret->byref && retTy->ty == Tstruct) { + fty.ret->rewrite = &integerRewrite; + fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); } + } - // extern(C) and all others - else - { - // RETURN VALUE + // IMPLICIT PARAMETERS - if ((!fty.ret->byref && isMagicCLong(tf->next)) || - tf->next->toBasetype() == Type::tcomplex32) - { - // __c_long -> i32, cfloat -> i64 - fty.ret->rewrite = &integerRewrite; - fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); - } - else if (isOSX) - { - // value struct returns should be rewritten as an int type to - // generate correct register usage (matches clang). - // note: sret functions change ret type to void so this won't - // trigger for those - Type* retTy = fty.ret->type->toBasetype(); - if (!fty.ret->byref && retTy->ty == Tstruct) - { - fty.ret->rewrite = &integerRewrite; - fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); - } - } - - // IMPLICIT PARAMETERS - - // EXPLICIT PARAMETERS - - // Clang does not pass empty structs, while it seems that GCC does, - // at least on Linux x86. We don't know whether the C compiler will - // be Clang or GCC, so just assume Clang on OS X and G++ on Linux. - if (isOSX) - { - size_t i = 0; - while (i < fty.args.size()) - { - Type *type = fty.args[i]->type->toBasetype(); - if (type->ty == Tstruct) - { - // Do not pass empty structs at all for C++ ABI compatibility. - // Tests with clang reveal that more complex "empty" types, for - // example a struct containing an empty struct, are not - // optimized in the same way. - StructDeclaration *sd = static_cast(type)->sym; - if (sd->fields.empty()) - { - fty.args.erase(fty.args.begin() + i); - continue; - } - } - ++i; - } - } - - for (size_t i = 0; i < fty.args.size(); ++i) - { - IrFuncTyArg *arg = fty.args[i]; - if (!arg->byref && isMagicCLong(arg->type)) - { - arg->rewrite = &integerRewrite; - arg->ltype = integerRewrite.type(arg->type, arg->ltype); - arg->byref = false; - arg->attrs.clear(); - } + // EXPLICIT PARAMETERS + + // Clang does not pass empty structs, while it seems that GCC does, + // at least on Linux x86. We don't know whether the C compiler will + // be Clang or GCC, so just assume Clang on OS X and G++ on Linux. + if (isOSX) { + size_t i = 0; + while (i < fty.args.size()) { + Type *type = fty.args[i]->type->toBasetype(); + if (type->ty == Tstruct) { + // Do not pass empty structs at all for C++ ABI compatibility. + // Tests with clang reveal that more complex "empty" types, for + // example a struct containing an empty struct, are not + // optimized in the same way. + StructDeclaration *sd = static_cast(type)->sym; + if (sd->fields.empty()) { + fty.args.erase(fty.args.begin() + i); + continue; } + } + ++i; } + } + + for (size_t i = 0; i < fty.args.size(); ++i) { + IrFuncTyArg *arg = fty.args[i]; + if (!arg->byref && isMagicCLong(arg->type)) { + arg->rewrite = &integerRewrite; + arg->ltype = integerRewrite.type(arg->type, arg->ltype); + arg->byref = false; + arg->attrs.clear(); + } + } } + } }; // The public getter for abi.cpp. -TargetABI* getX86TargetABI() { - return new X86TargetABI; -} +TargetABI *getX86TargetABI() { return new X86TargetABI; } diff --git a/gen/abi-x86.h b/gen/abi-x86.h index bd3312e39f..7556cdd955 100644 --- a/gen/abi-x86.h +++ b/gen/abi-x86.h @@ -15,6 +15,6 @@ #define LDC_GEN_ABI_X86_H struct TargetABI; -TargetABI* getX86TargetABI(); +TargetABI *getX86TargetABI(); #endif diff --git a/gen/abi.cpp b/gen/abi.cpp index 2da2d8d92a..c648e42aec 100644 --- a/gen/abi.cpp +++ b/gen/abi.cpp @@ -28,242 +28,215 @@ ////////////////////////////////////////////////////////////////////////////// -void ABIRewrite::getL(Type* dty, LLValue* v, LLValue* lval) -{ - LLValue* rval = get(dty, v); - assert(rval->getType() == lval->getType()->getContainedType(0)); - DtoStore(rval, lval); +void ABIRewrite::getL(Type *dty, LLValue *v, LLValue *lval) { + LLValue *rval = get(dty, v); + assert(rval->getType() == lval->getType()->getContainedType(0)); + DtoStore(rval, lval); } ////////////////////////////////////////////////////////////////////////////// -LLValue* ABIRewrite::getAddressOf(DValue* v) -{ - Type* dty = v->getType(); - if (DtoIsPassedByRef(dty)) - { - // v is lowered to a LL pointer to the struct/static array - return v->getRVal(); - } +LLValue *ABIRewrite::getAddressOf(DValue *v) { + Type *dty = v->getType(); + if (DtoIsPassedByRef(dty)) { + // v is lowered to a LL pointer to the struct/static array + return v->getRVal(); + } - if (v->isLVal()) - return v->getLVal(); + if (v->isLVal()) + return v->getLVal(); - return DtoAllocaDump(v, ".getAddressOf_dump"); + return DtoAllocaDump(v, ".getAddressOf_dump"); } -void ABIRewrite::storeToMemory(LLValue* rval, LLValue* address) -{ - LLType* pointerType = address->getType(); - assert(pointerType->isPointerTy()); - LLType* pointeeType = pointerType->getPointerElementType(); +void ABIRewrite::storeToMemory(LLValue *rval, LLValue *address) { + LLType *pointerType = address->getType(); + assert(pointerType->isPointerTy()); + LLType *pointeeType = pointerType->getPointerElementType(); - LLType* rvalType = rval->getType(); - if (rvalType != pointeeType) - { - if (getTypeStoreSize(rvalType) > getTypeAllocSize(pointeeType)) - { - // not enough allocated memory - LLValue* paddedDump = DtoAllocaDump(rval, 0, ".storeToMemory_paddedDump"); - DtoAggrCopy(address, paddedDump); - return; - } - - address = DtoBitCast(address, getPtrToType(rvalType), ".storeToMemory_bitCastAddress"); + LLType *rvalType = rval->getType(); + if (rvalType != pointeeType) { + if (getTypeStoreSize(rvalType) > getTypeAllocSize(pointeeType)) { + // not enough allocated memory + LLValue *paddedDump = DtoAllocaDump(rval, 0, ".storeToMemory_paddedDump"); + DtoAggrCopy(address, paddedDump); + return; } - DtoStore(rval, address); + address = DtoBitCast(address, getPtrToType(rvalType), + ".storeToMemory_bitCastAddress"); + } + + DtoStore(rval, address); } -LLValue* ABIRewrite::loadFromMemory(LLValue* address, LLType* asType, const char* name) -{ - LLType* pointerType = address->getType(); - assert(pointerType->isPointerTy()); - LLType* pointeeType = pointerType->getPointerElementType(); +LLValue *ABIRewrite::loadFromMemory(LLValue *address, LLType *asType, + const char *name) { + LLType *pointerType = address->getType(); + assert(pointerType->isPointerTy()); + LLType *pointeeType = pointerType->getPointerElementType(); - if (asType == pointeeType) - return DtoLoad(address, name); - - if (getTypeStoreSize(asType) > getTypeAllocSize(pointeeType)) - { - // not enough allocated memory - LLValue* paddedDump = DtoRawAlloca(asType, 0, ".loadFromMemory_paddedDump"); - DtoMemCpy(paddedDump, address, DtoConstSize_t(getTypeAllocSize(pointeeType))); - return DtoLoad(paddedDump, name); - } - - address = DtoBitCast(address, getPtrToType(asType), ".loadFromMemory_bitCastAddress"); + if (asType == pointeeType) return DtoLoad(address, name); + + if (getTypeStoreSize(asType) > getTypeAllocSize(pointeeType)) { + // not enough allocated memory + LLValue *paddedDump = DtoRawAlloca(asType, 0, ".loadFromMemory_paddedDump"); + DtoMemCpy(paddedDump, address, + DtoConstSize_t(getTypeAllocSize(pointeeType))); + return DtoLoad(paddedDump, name); + } + + address = DtoBitCast(address, getPtrToType(asType), + ".loadFromMemory_bitCastAddress"); + return DtoLoad(address, name); } ////////////////////////////////////////////////////////////////////////////// -void TargetABI::rewriteVarargs(IrFuncTy& fty, std::vector& args) -{ - for (unsigned i = 0; i < args.size(); ++i) - { - IrFuncTyArg& arg = *args[i]; - if (!arg.byref) // don't rewrite ByVal arguments - rewriteArgument(fty, arg); - } +void TargetABI::rewriteVarargs(IrFuncTy &fty, + std::vector &args) { + for (unsigned i = 0; i < args.size(); ++i) { + IrFuncTyArg &arg = *args[i]; + if (!arg.byref) // don't rewrite ByVal arguments + rewriteArgument(fty, arg); + } } ////////////////////////////////////////////////////////////////////////////// -LLValue* TargetABI::prepareVaStart(LLValue* pAp) -{ - // pass a void* pointer to ap to LLVM's va_start intrinsic - return DtoBitCast(pAp, getVoidPtrType()); +LLValue *TargetABI::prepareVaStart(LLValue *pAp) { + // pass a void* pointer to ap to LLVM's va_start intrinsic + return DtoBitCast(pAp, getVoidPtrType()); } ////////////////////////////////////////////////////////////////////////////// -void TargetABI::vaCopy(LLValue* pDest, LLValue* src) -{ - // simply bitcopy src over dest - DtoStore(src, pDest); +void TargetABI::vaCopy(LLValue *pDest, LLValue *src) { + // simply bitcopy src over dest + DtoStore(src, pDest); } ////////////////////////////////////////////////////////////////////////////// -LLValue* TargetABI::prepareVaArg(LLValue* pAp) -{ - // pass a void* pointer to ap to LLVM's va_arg intrinsic - return DtoBitCast(pAp, getVoidPtrType()); +LLValue *TargetABI::prepareVaArg(LLValue *pAp) { + // pass a void* pointer to ap to LLVM's va_arg intrinsic + return DtoBitCast(pAp, getVoidPtrType()); } ////////////////////////////////////////////////////////////////////////////// -Type* TargetABI::vaListType() -{ - // char* is used by default in druntime. - return Type::tchar->pointerTo(); +Type *TargetABI::vaListType() { + // char* is used by default in druntime. + return Type::tchar->pointerTo(); } ////////////////////////////////////////////////////////////////////////////// // Some reasonable defaults for when we don't know what ABI to use. -struct UnknownTargetABI : TargetABI -{ - bool returnInArg(TypeFunction* tf) - { - if (tf->isref) - return false; +struct UnknownTargetABI : TargetABI { + bool returnInArg(TypeFunction *tf) { + if (tf->isref) + return false; - // Return structs and static arrays on the stack. The latter is needed - // because otherwise LLVM tries to actually return the array in a number - // of physical registers, which leads, depending on the target, to - // either horrendous codegen or backend crashes. - Type* rt = tf->next->toBasetype(); - return (rt->ty == Tstruct || rt->ty == Tsarray); - } + // Return structs and static arrays on the stack. The latter is needed + // because otherwise LLVM tries to actually return the array in a number + // of physical registers, which leads, depending on the target, to + // either horrendous codegen or backend crashes. + Type *rt = tf->next->toBasetype(); + return (rt->ty == Tstruct || rt->ty == Tsarray); + } - bool passByVal(Type* t) - { - return t->toBasetype()->ty == Tstruct; - } + bool passByVal(Type *t) { return t->toBasetype()->ty == Tstruct; } - void rewriteFunctionType(TypeFunction* t, IrFuncTy &fty) - { - // why? - } + void rewriteFunctionType(TypeFunction *t, IrFuncTy &fty) { + // why? + } }; ////////////////////////////////////////////////////////////////////////////// -TargetABI * TargetABI::getTarget() -{ - switch (global.params.targetTriple.getArch()) - { - case llvm::Triple::x86: - return getX86TargetABI(); - case llvm::Triple::x86_64: - if (global.params.targetTriple.isOSWindows()) - return getWin64TargetABI(); - else - return getX86_64TargetABI(); - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - return getMIPS64TargetABI(global.params.is64bit); - case llvm::Triple::ppc64: - case llvm::Triple::ppc64le: - return getPPC64TargetABI(global.params.targetTriple.isArch64Bit()); +TargetABI *TargetABI::getTarget() { + switch (global.params.targetTriple.getArch()) { + case llvm::Triple::x86: + return getX86TargetABI(); + case llvm::Triple::x86_64: + if (global.params.targetTriple.isOSWindows()) + return getWin64TargetABI(); + else + return getX86_64TargetABI(); + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + return getMIPS64TargetABI(global.params.is64bit); + case llvm::Triple::ppc64: + case llvm::Triple::ppc64le: + return getPPC64TargetABI(global.params.targetTriple.isArch64Bit()); #if LDC_LLVM_VER == 305 - case llvm::Triple::arm64: - case llvm::Triple::arm64_be: + case llvm::Triple::arm64: + case llvm::Triple::arm64_be: #endif - case llvm::Triple::aarch64: - case llvm::Triple::aarch64_be: - return getAArch64TargetABI(); - default: - Logger::cout() << "WARNING: Unknown ABI, guessing...\n"; - return new UnknownTargetABI; - } + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_be: + return getAArch64TargetABI(); + default: + Logger::cout() << "WARNING: Unknown ABI, guessing...\n"; + return new UnknownTargetABI; + } } ////////////////////////////////////////////////////////////////////////////// // A simple ABI for LLVM intrinsics. -struct IntrinsicABI : TargetABI -{ - RemoveStructPadding remove_padding; +struct IntrinsicABI : TargetABI { + RemoveStructPadding remove_padding; - bool returnInArg(TypeFunction* tf) - { - return false; + bool returnInArg(TypeFunction *tf) { return false; } + + bool passByVal(Type *t) { return false; } + + void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) { + Type *ty = arg.type->toBasetype(); + if (ty->ty != Tstruct) + return; + // TODO: Check that no unions are passed in or returned. + + LLType *abiTy = DtoUnpaddedStructType(arg.type); + + if (abiTy && abiTy != arg.ltype) { + arg.ltype = abiTy; + arg.rewrite = &remove_padding; + } + } + + void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) { + if (!fty.arg_sret) { + Type *rt = fty.ret->type->toBasetype(); + if (rt->ty == Tstruct) { + Logger::println("Intrinsic ABI: Transforming return type"); + rewriteArgument(fty, *fty.ret); + } } - bool passByVal(Type* t) - { - return false; - } - - void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg) - { - Type* ty = arg.type->toBasetype(); - if (ty->ty != Tstruct) - return; - // TODO: Check that no unions are passed in or returned. - - LLType* abiTy = DtoUnpaddedStructType(arg.type); - - if (abiTy && abiTy != arg.ltype) { - arg.ltype = abiTy; - arg.rewrite = &remove_padding; - } - } - - void rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) - { - if (!fty.arg_sret) { - Type* rt = fty.ret->type->toBasetype(); - if (rt->ty == Tstruct) { - Logger::println("Intrinsic ABI: Transforming return type"); - rewriteArgument(fty, *fty.ret); - } - } - - Logger::println("Intrinsic ABI: Transforming arguments"); - LOG_SCOPE; - - for (auto arg : fty.args) { - IF_LOG Logger::cout() << "Arg: " << arg->type->toChars() << '\n'; - - // Arguments that are in memory are of no interest to us. - if (arg->byref) - continue; - - rewriteArgument(fty, *arg); - - IF_LOG Logger::cout() << "New arg type: " << *arg->ltype << '\n'; - } + Logger::println("Intrinsic ABI: Transforming arguments"); + LOG_SCOPE; + + for (auto arg : fty.args) { + IF_LOG Logger::cout() << "Arg: " << arg->type->toChars() << '\n'; + + // Arguments that are in memory are of no interest to us. + if (arg->byref) + continue; + + rewriteArgument(fty, *arg); + + IF_LOG Logger::cout() << "New arg type: " << *arg->ltype << '\n'; } + } }; -TargetABI * TargetABI::getIntrinsic() -{ - static IntrinsicABI iabi; - return &iabi; +TargetABI *TargetABI::getIntrinsic() { + static IntrinsicABI iabi; + return &iabi; } diff --git a/gen/abi.h b/gen/abi.h index 9f7fa82639..b2071975ef 100644 --- a/gen/abi.h +++ b/gen/abi.h @@ -27,107 +27,109 @@ struct IrFuncTy; struct IrFuncTyArg; class DValue; -namespace llvm -{ - class Type; - class Value; - class FunctionType; +namespace llvm { +class Type; +class Value; +class FunctionType; } // return rewrite rule -struct ABIRewrite -{ - virtual ~ABIRewrite() {} +struct ABIRewrite { + virtual ~ABIRewrite() {} - /// get a rewritten value back to its original form - virtual llvm::Value* get(Type* dty, llvm::Value* v) = 0; + /// get a rewritten value back to its original form + virtual llvm::Value *get(Type *dty, llvm::Value *v) = 0; - /// get a rewritten value back to its original form and store result in provided lvalue - /// this one is optional and defaults to calling the one above - virtual void getL(Type* dty, llvm::Value* v, llvm::Value* lval); + /// get a rewritten value back to its original form and store result in + /// provided lvalue + /// this one is optional and defaults to calling the one above + virtual void getL(Type *dty, llvm::Value *v, llvm::Value *lval); - /// put out rewritten value - virtual llvm::Value* put(DValue* v) = 0; + /// put out rewritten value + virtual llvm::Value *put(DValue *v) = 0; - /// should return the transformed type for this rewrite - virtual llvm::Type* type(Type* dty, llvm::Type* t) = 0; + /// should return the transformed type for this rewrite + virtual llvm::Type *type(Type *dty, llvm::Type *t) = 0; protected: - /***** Static Helpers *****/ + /***** Static Helpers *****/ - // Returns the address of a D value, storing it to memory first if need be. - static llvm::Value* getAddressOf(DValue* v); + // Returns the address of a D value, storing it to memory first if need be. + static llvm::Value *getAddressOf(DValue *v); - // Stores a LL value to a specified memory address. The element type of the provided - // pointer doesn't need to match the value type (=> suited for bit-casting). - static void storeToMemory(llvm::Value* rval, llvm::Value* address); + // Stores a LL value to a specified memory address. The element type of the + // provided + // pointer doesn't need to match the value type (=> suited for bit-casting). + static void storeToMemory(llvm::Value *rval, llvm::Value *address); - // Loads a LL value of a specified type from memory. The element type of the provided - // pointer doesn't need to match the value type (=> suited for bit-casting). - static llvm::Value* loadFromMemory(llvm::Value* address, llvm::Type* asType, - const char* name = ".bitcast_result"); + // Loads a LL value of a specified type from memory. The element type of the + // provided + // pointer doesn't need to match the value type (=> suited for bit-casting). + static llvm::Value *loadFromMemory(llvm::Value *address, llvm::Type *asType, + const char *name = ".bitcast_result"); }; // interface called by codegen -struct TargetABI -{ - virtual ~TargetABI() {} +struct TargetABI { + virtual ~TargetABI() {} - /// Returns the ABI for the target we're compiling for - static TargetABI* getTarget(); + /// Returns the ABI for the target we're compiling for + static TargetABI *getTarget(); - /// Returns the ABI for intrinsics - static TargetABI* getIntrinsic(); + /// Returns the ABI for intrinsics + static TargetABI *getIntrinsic(); - /// Returns the LLVM calling convention to be used for the given D linkage - /// type on the target. Defaults to the C calling convention. - virtual llvm::CallingConv::ID callingConv(llvm::FunctionType* ft, LINK l) - { - return llvm::CallingConv::C; - } + /// Returns the LLVM calling convention to be used for the given D linkage + /// type on the target. Defaults to the C calling convention. + virtual llvm::CallingConv::ID callingConv(llvm::FunctionType *ft, LINK l) { + return llvm::CallingConv::C; + } - /// Applies any rewrites that might be required to accurately reproduce the - /// passed function name on LLVM given a specific calling convention. - /// - /// Using this function at a stage where the name could be user-visible is - /// almost certainly a mistake; it is intended to e.g. prepend '\1' where - /// disabling the LLVM-internal name mangling/postprocessing is required. - virtual std::string mangleForLLVM(llvm::StringRef name, LINK l) { return name; } + /// Applies any rewrites that might be required to accurately reproduce the + /// passed function name on LLVM given a specific calling convention. + /// + /// Using this function at a stage where the name could be user-visible is + /// almost certainly a mistake; it is intended to e.g. prepend '\1' where + /// disabling the LLVM-internal name mangling/postprocessing is required. + virtual std::string mangleForLLVM(llvm::StringRef name, LINK l) { + return name; + } - /// Returns true if the function uses sret (struct return), - /// meaning that it gets a hidden pointer to a struct which has been pre- - /// allocated by the caller. - virtual bool returnInArg(TypeFunction* tf) = 0; + /// Returns true if the function uses sret (struct return), + /// meaning that it gets a hidden pointer to a struct which has been pre- + /// allocated by the caller. + virtual bool returnInArg(TypeFunction *tf) = 0; - /// Returns true if the type is passed by value - virtual bool passByVal(Type* t) = 0; + /// Returns true if the type is passed by value + virtual bool passByVal(Type *t) = 0; - // Returns true if the 'this' argument is to be passed before the 'sret' argument. - virtual bool passThisBeforeSret(TypeFunction* tf) { return false; } + // Returns true if the 'this' argument is to be passed before the 'sret' + // argument. + virtual bool passThisBeforeSret(TypeFunction *tf) { return false; } - /// Called to give ABI the chance to rewrite the types - virtual void rewriteFunctionType(TypeFunction* t, IrFuncTy& fty) = 0; - virtual void rewriteVarargs(IrFuncTy& fty, std::vector& args); - virtual void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg) {} + /// Called to give ABI the chance to rewrite the types + virtual void rewriteFunctionType(TypeFunction *t, IrFuncTy &fty) = 0; + virtual void rewriteVarargs(IrFuncTy &fty, std::vector &args); + virtual void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) {} - // Prepares a va_start intrinsic call. - // Input: pointer to passed ap argument (va_list*) - // Output: value to be passed to LLVM's va_start intrinsic (void*) - virtual llvm::Value* prepareVaStart(llvm::Value* pAp); + // Prepares a va_start intrinsic call. + // Input: pointer to passed ap argument (va_list*) + // Output: value to be passed to LLVM's va_start intrinsic (void*) + virtual llvm::Value *prepareVaStart(llvm::Value *pAp); - // Implements the va_copy intrinsic. - // Input: pointer to dest argument (va_list*) and src argument (va_list) - virtual void vaCopy(llvm::Value* pDest, llvm::Value* src); + // Implements the va_copy intrinsic. + // Input: pointer to dest argument (va_list*) and src argument (va_list) + virtual void vaCopy(llvm::Value *pDest, llvm::Value *src); - // Prepares a va_arg intrinsic call. - // Input: pointer to passed ap argument (va_list*) - // Output: value to be passed to LLVM's va_arg intrinsic (void*) - virtual llvm::Value* prepareVaArg(llvm::Value* pAp); + // Prepares a va_arg intrinsic call. + // Input: pointer to passed ap argument (va_list*) + // Output: value to be passed to LLVM's va_arg intrinsic (void*) + virtual llvm::Value *prepareVaArg(llvm::Value *pAp); - /// Returns the D type to be used for va_list. - /// - /// Must match the alias in druntime. - virtual Type* vaListType(); + /// Returns the D type to be used for va_list. + /// + /// Must match the alias in druntime. + virtual Type *vaListType(); }; #endif diff --git a/gen/arrays.cpp b/gen/arrays.cpp index cf2c0fd2ee..42c40fe80e 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -25,1213 +25,1164 @@ #include "ir/irfunction.h" #include "ir/irmodule.h" -static void DtoSetArray(DValue* array, LLValue* dim, LLValue* ptr); +static void DtoSetArray(DValue *array, LLValue *dim, LLValue *ptr); ////////////////////////////////////////////////////////////////////////////////////////// -static LLValue *DtoSlice(DValue *dval) -{ - LLValue *val = dval->getRVal(); - if (dval->getType()->toBasetype()->ty == Tsarray) { - // Convert static array to slice - LLStructType *type = DtoArrayType(LLType::getInt8Ty(gIR->context())); - LLValue *array = DtoRawAlloca(type, 0, ".array"); - DtoStore(DtoArrayLen(dval), DtoGEPi(array, 0, 0, ".len")); - DtoStore(DtoBitCast(val, getVoidPtrType()), DtoGEPi(array, 0, 1, ".ptr")); - val = DtoLoad(array); - } - return val; -} - -static LLValue *DtoSlice(LLValue *ptr, LLValue *length, LLType *elemType = NULL) -{ - if (elemType == NULL) - elemType = ptr->getType()->getContainedType(0); - elemType = i1ToI8(voidToI8(elemType)); - - LLStructType *type = DtoArrayType(elemType); - LLValue *array = DtoRawAlloca(type, 0, ".array"); - DtoStore(length, DtoGEPi(array, 0, 0)); - DtoStore(DtoBitCast(ptr, elemType->getPointerTo()), DtoGEPi(array, 0, 1)); - return DtoLoad(array); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -static LLValue *DtoSlicePtr(DValue *dval) -{ - Loc loc; +static LLValue *DtoSlice(DValue *dval) { + LLValue *val = dval->getRVal(); + if (dval->getType()->toBasetype()->ty == Tsarray) { + // Convert static array to slice LLStructType *type = DtoArrayType(LLType::getInt8Ty(gIR->context())); - Type *vt = dval->getType()->toBasetype(); - if (vt->ty == Tarray) - return makeLValue(loc, dval); - - bool isStaticArray = vt->ty == Tsarray; - LLValue *val = isStaticArray ? dval->getRVal() : makeLValue(loc, dval); LLValue *array = DtoRawAlloca(type, 0, ".array"); - LLValue *len = isStaticArray ? DtoArrayLen(dval) : DtoConstSize_t(1); - DtoStore(len, DtoGEPi(array, 0, 0)); - DtoStore(DtoBitCast(val, getVoidPtrType()), DtoGEPi(array, 0, 1)); - return array; + DtoStore(DtoArrayLen(dval), DtoGEPi(array, 0, 0, ".len")); + DtoStore(DtoBitCast(val, getVoidPtrType()), DtoGEPi(array, 0, 1, ".ptr")); + val = DtoLoad(array); + } + return val; +} + +static LLValue *DtoSlice(LLValue *ptr, LLValue *length, + LLType *elemType = NULL) { + if (elemType == NULL) + elemType = ptr->getType()->getContainedType(0); + elemType = i1ToI8(voidToI8(elemType)); + + LLStructType *type = DtoArrayType(elemType); + LLValue *array = DtoRawAlloca(type, 0, ".array"); + DtoStore(length, DtoGEPi(array, 0, 0)); + DtoStore(DtoBitCast(ptr, elemType->getPointerTo()), DtoGEPi(array, 0, 1)); + return DtoLoad(array); } ////////////////////////////////////////////////////////////////////////////////////////// -LLStructType* DtoArrayType(Type* arrayTy) -{ - assert(arrayTy->nextOf()); - llvm::Type* elems[] = { DtoSize_t(), DtoPtrToType(arrayTy->nextOf()) }; - return llvm::StructType::get(gIR->context(), elems, false); -} +static LLValue *DtoSlicePtr(DValue *dval) { + Loc loc; + LLStructType *type = DtoArrayType(LLType::getInt8Ty(gIR->context())); + Type *vt = dval->getType()->toBasetype(); + if (vt->ty == Tarray) + return makeLValue(loc, dval); -LLStructType* DtoArrayType(LLType* t) -{ - llvm::Type* elems[] = { DtoSize_t(), getPtrToType(t) }; - return llvm::StructType::get(gIR->context(), elems, false); + bool isStaticArray = vt->ty == Tsarray; + LLValue *val = isStaticArray ? dval->getRVal() : makeLValue(loc, dval); + LLValue *array = DtoRawAlloca(type, 0, ".array"); + LLValue *len = isStaticArray ? DtoArrayLen(dval) : DtoConstSize_t(1); + DtoStore(len, DtoGEPi(array, 0, 0)); + DtoStore(DtoBitCast(val, getVoidPtrType()), DtoGEPi(array, 0, 1)); + return array; } ////////////////////////////////////////////////////////////////////////////////////////// -LLArrayType* DtoStaticArrayType(Type* t) -{ - t = t->toBasetype(); - assert(t->ty == Tsarray); - TypeSArray* tsa = static_cast(t); - Type* tnext = tsa->nextOf(); +LLStructType *DtoArrayType(Type *arrayTy) { + assert(arrayTy->nextOf()); + llvm::Type *elems[] = {DtoSize_t(), DtoPtrToType(arrayTy->nextOf())}; + return llvm::StructType::get(gIR->context(), elems, false); +} - return LLArrayType::get(DtoMemType(tnext), tsa->dim->toUInteger()); +LLStructType *DtoArrayType(LLType *t) { + llvm::Type *elems[] = {DtoSize_t(), getPtrToType(t)}; + return llvm::StructType::get(gIR->context(), elems, false); } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoSetArrayToNull(LLValue* v) -{ - IF_LOG Logger::println("DtoSetArrayToNull"); - LOG_SCOPE; +LLArrayType *DtoStaticArrayType(Type *t) { + t = t->toBasetype(); + assert(t->ty == Tsarray); + TypeSArray *tsa = static_cast(t); + Type *tnext = tsa->nextOf(); - assert(isaPointer(v)); - LLType* t = v->getType()->getContainedType(0); - - DtoStore(LLConstant::getNullValue(t), v); + return LLArrayType::get(DtoMemType(tnext), tsa->dim->toUInteger()); } ////////////////////////////////////////////////////////////////////////////////////////// -static void DtoArrayInit(Loc& loc, LLValue* ptr, LLValue* length, DValue* dvalue, int op) -{ - IF_LOG Logger::println("DtoArrayInit"); - LOG_SCOPE; +void DtoSetArrayToNull(LLValue *v) { + IF_LOG Logger::println("DtoSetArrayToNull"); + LOG_SCOPE; - LLValue* value = dvalue->getRVal(); - LLValue* elementSize = DtoConstSize_t(getTypePaddedSize(value->getType())); + assert(isaPointer(v)); + LLType *t = v->getType()->getContainedType(0); - // lets first optimize all zero/constant i8 initializations down to a memset. - // this simplifies codegen later on as llvm null's have no address! - if (isaConstant(value) && (isaConstant(value)->isNullValue() - || value->getType() == LLType::getInt8Ty(gIR->context()))) - { - LLValue* nbytes = gIR->ir->CreateMul(length, elementSize, ".nbytes"); - if (isaConstant(value)->isNullValue()) - DtoMemSetZero(ptr, nbytes); - else - DtoMemSet(ptr, value, nbytes); - return; - } - - // create blocks - llvm::BasicBlock* condbb = llvm::BasicBlock::Create(gIR->context(), "arrayinit.cond", - gIR->topfunc()); - llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(gIR->context(), "arrayinit.body", - gIR->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "arrayinit.end", - gIR->topfunc()); - - // initialize iterator - LLValue *itr = DtoAllocaDump(DtoConstSize_t(0), 0, "arrayinit.itr"); - - // move into the for condition block, ie. start the loop - assert(!gIR->scopereturned()); - llvm::BranchInst::Create(condbb, gIR->scopebb()); - - // replace current scope - gIR->scope() = IRScope(condbb); - - // create the condition - LLValue* cond_val = gIR->ir->CreateICmpNE(DtoLoad(itr), length, "arrayinit.condition"); - - // conditional branch - assert(!gIR->scopereturned()); - llvm::BranchInst::Create(bodybb, endbb, cond_val, gIR->scopebb()); - - // rewrite scope - gIR->scope() = IRScope(bodybb); - - LLValue* itr_val = DtoLoad(itr); - // assign array element value - DValue *arrayelem = new DVarValue(dvalue->type->toBasetype(), DtoGEP1(ptr, itr_val, "arrayinit.arrayelem")); - DtoAssign(loc, arrayelem, dvalue, op); - - // increment iterator - DtoStore(gIR->ir->CreateAdd(itr_val, DtoConstSize_t(1), "arrayinit.new_itr"), itr); - - // loop - llvm::BranchInst::Create(condbb, gIR->scopebb()); - - // rewrite the scope - gIR->scope() = IRScope(endbb); + DtoStore(LLConstant::getNullValue(t), v); } ////////////////////////////////////////////////////////////////////////////////////////// -static Type *DtoArrayElementType(Type *arrayType) -{ - assert(arrayType->toBasetype()->nextOf()); - Type *t = arrayType->toBasetype()->nextOf()->toBasetype(); - while (t->ty == Tsarray) - t = t->nextOf()->toBasetype(); - return t; -} +static void DtoArrayInit(Loc &loc, LLValue *ptr, LLValue *length, + DValue *dvalue, int op) { + IF_LOG Logger::println("DtoArrayInit"); + LOG_SCOPE; -////////////////////////////////////////////////////////////////////////////////////////// + LLValue *value = dvalue->getRVal(); + LLValue *elementSize = DtoConstSize_t(getTypePaddedSize(value->getType())); -static void copySlice(Loc& loc, LLValue* dstarr, LLValue* sz1, LLValue* srcarr, LLValue* sz2, bool knownInBounds) -{ - const bool checksEnabled = global.params.useAssert || gIR->emitArrayBoundsChecks(); - if (checksEnabled && !knownInBounds) - { - LLValue* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_array_slice_copy"); - gIR->CreateCallOrInvoke(fn, dstarr, sz1, srcarr, sz2); - } + // lets first optimize all zero/constant i8 initializations down to a memset. + // this simplifies codegen later on as llvm null's have no address! + if (isaConstant(value) && + (isaConstant(value)->isNullValue() || + value->getType() == LLType::getInt8Ty(gIR->context()))) { + LLValue *nbytes = gIR->ir->CreateMul(length, elementSize, ".nbytes"); + if (isaConstant(value)->isNullValue()) + DtoMemSetZero(ptr, nbytes); else - { - // We might have dstarr == srcarr at compile time, but as long as - // sz1 == 0 at runtime, this would probably still be legal (the C spec - // is unclear here). - DtoMemCpy(dstarr, srcarr, sz1); - } + DtoMemSet(ptr, value, nbytes); + return; + } + + // create blocks + llvm::BasicBlock *condbb = llvm::BasicBlock::Create( + gIR->context(), "arrayinit.cond", gIR->topfunc()); + llvm::BasicBlock *bodybb = llvm::BasicBlock::Create( + gIR->context(), "arrayinit.body", gIR->topfunc()); + llvm::BasicBlock *endbb = + llvm::BasicBlock::Create(gIR->context(), "arrayinit.end", gIR->topfunc()); + + // initialize iterator + LLValue *itr = DtoAllocaDump(DtoConstSize_t(0), 0, "arrayinit.itr"); + + // move into the for condition block, ie. start the loop + assert(!gIR->scopereturned()); + llvm::BranchInst::Create(condbb, gIR->scopebb()); + + // replace current scope + gIR->scope() = IRScope(condbb); + + // create the condition + LLValue *cond_val = + gIR->ir->CreateICmpNE(DtoLoad(itr), length, "arrayinit.condition"); + + // conditional branch + assert(!gIR->scopereturned()); + llvm::BranchInst::Create(bodybb, endbb, cond_val, gIR->scopebb()); + + // rewrite scope + gIR->scope() = IRScope(bodybb); + + LLValue *itr_val = DtoLoad(itr); + // assign array element value + DValue *arrayelem = new DVarValue( + dvalue->type->toBasetype(), DtoGEP1(ptr, itr_val, "arrayinit.arrayelem")); + DtoAssign(loc, arrayelem, dvalue, op); + + // increment iterator + DtoStore(gIR->ir->CreateAdd(itr_val, DtoConstSize_t(1), "arrayinit.new_itr"), + itr); + + // loop + llvm::BranchInst::Create(condbb, gIR->scopebb()); + + // rewrite the scope + gIR->scope() = IRScope(endbb); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +static Type *DtoArrayElementType(Type *arrayType) { + assert(arrayType->toBasetype()->nextOf()); + Type *t = arrayType->toBasetype()->nextOf()->toBasetype(); + while (t->ty == Tsarray) + t = t->nextOf()->toBasetype(); + return t; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +static void copySlice(Loc &loc, LLValue *dstarr, LLValue *sz1, LLValue *srcarr, + LLValue *sz2, bool knownInBounds) { + const bool checksEnabled = + global.params.useAssert || gIR->emitArrayBoundsChecks(); + if (checksEnabled && !knownInBounds) { + LLValue *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_array_slice_copy"); + gIR->CreateCallOrInvoke(fn, dstarr, sz1, srcarr, sz2); + } else { + // We might have dstarr == srcarr at compile time, but as long as + // sz1 == 0 at runtime, this would probably still be legal (the C spec + // is unclear here). + DtoMemCpy(dstarr, srcarr, sz1); + } } ////////////////////////////////////////////////////////////////////////////////////////// // Determine whether t is an array of structs that need a postblit. -static bool arrayNeedsPostblit(Type *t) -{ - t = DtoArrayElementType(t); - if (t->ty == Tstruct) - return static_cast(t)->sym->postblit != NULL; - return false; +static bool arrayNeedsPostblit(Type *t) { + t = DtoArrayElementType(t); + if (t->ty == Tstruct) + return static_cast(t)->sym->postblit != NULL; + return false; } -// Does array assignment (or initialization) from another array of the same element type +// Does array assignment (or initialization) from another array of the same +// element type // or from an appropriate single element. -void DtoArrayAssign(Loc& loc, DValue* lhs, DValue* rhs, int op, bool canSkipPostblit) -{ - IF_LOG Logger::println("DtoArrayAssign"); - LOG_SCOPE; +void DtoArrayAssign(Loc &loc, DValue *lhs, DValue *rhs, int op, + bool canSkipPostblit) { + IF_LOG Logger::println("DtoArrayAssign"); + LOG_SCOPE; - Type* t = lhs->type->toBasetype(); - Type* t2 = rhs->type->toBasetype(); - assert(t->nextOf()); + Type *t = lhs->type->toBasetype(); + Type *t2 = rhs->type->toBasetype(); + assert(t->nextOf()); - // reference assignment for dynamic array? - if (t->ty == Tarray && !lhs->isSlice()) - { - assert(t2->ty == Tarray || t2->ty == Tsarray); - if (rhs->isNull()) - DtoSetArrayToNull(lhs->getLVal()); - else - DtoSetArray(lhs, DtoArrayLen(rhs), DtoArrayPtr(rhs)); - return; - } - - // TOKblit is generated by the frontend for (default) initialization of - // static arrays of structs with a single element. - const bool isConstructing = (op == TOKconstruct || op == TOKblit); - - Type* const elemType = t->nextOf()->toBasetype(); - const bool needsDestruction = (!isConstructing && elemType->needsDestruction()); - const bool needsPostblit = (op != TOKblit && !canSkipPostblit && arrayNeedsPostblit(t)); - - LLValue* realLhsPtr = DtoArrayPtr(lhs); - LLValue* lhsPtr = DtoBitCast(realLhsPtr, getVoidPtrType()); - LLValue* lhsLength = DtoArrayLen(lhs); - - // Be careful to handle void arrays correctly when modifying this (see tests - // for DMD issue 7493). - // TODO: This should use AssignExp::ismemset. - LLValue* realRhsArrayPtr = - (t2->ty == Tarray || t2->ty == Tsarray ? DtoArrayPtr(rhs) : NULL); - if (realRhsArrayPtr && realRhsArrayPtr->getType() == realLhsPtr->getType()) - { - // T[] = T[] T[] = T[n] - // T[n] = T[n] T[n] = T[] - LLValue* rhsPtr = DtoBitCast(realRhsArrayPtr, getVoidPtrType()); - LLValue* rhsLength = DtoArrayLen(rhs); - - if (!needsDestruction && !needsPostblit) - { - // fast version - LLValue* elemSize = DtoConstSize_t(getTypePaddedSize(DtoMemType(elemType))); - LLValue* lhsSize = gIR->ir->CreateMul(elemSize, lhsLength); - - if (rhs->isNull()) - { - DtoMemSetZero(lhsPtr, lhsSize); - } - else - { - LLValue* rhsSize = gIR->ir->CreateMul(elemSize, rhsLength); - const bool knownInBounds = isConstructing || - (t->ty == Tsarray && t2->ty == Tsarray); - copySlice(loc, lhsPtr, lhsSize, rhsPtr, rhsSize, knownInBounds); - } - } - else if (isConstructing) - { - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arrayctor"); - LLCallSite call = gIR->CreateCallOrInvoke( - fn, - DtoTypeInfoOf(elemType), - DtoSlice(rhsPtr, rhsLength), - DtoSlice(lhsPtr, lhsLength) - ); - call.setCallingConv(llvm::CallingConv::C); - } - else // assigning - { - LLValue* tmpSwap = DtoAlloca(elemType, "arrayAssign.tmpSwap"); - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, - !canSkipPostblit ? "_d_arrayassign_l" : "_d_arrayassign_r"); - LLCallSite call = gIR->CreateCallOrInvoke( - fn, - DtoTypeInfoOf(elemType), - DtoSlice(rhsPtr, rhsLength), - DtoSlice(lhsPtr, lhsLength), - DtoBitCast(tmpSwap, getVoidPtrType()) - ); - call.setCallingConv(llvm::CallingConv::C); - } - } + // reference assignment for dynamic array? + if (t->ty == Tarray && !lhs->isSlice()) { + assert(t2->ty == Tarray || t2->ty == Tsarray); + if (rhs->isNull()) + DtoSetArrayToNull(lhs->getLVal()); else + DtoSetArray(lhs, DtoArrayLen(rhs), DtoArrayPtr(rhs)); + return; + } + + // TOKblit is generated by the frontend for (default) initialization of + // static arrays of structs with a single element. + const bool isConstructing = (op == TOKconstruct || op == TOKblit); + + Type *const elemType = t->nextOf()->toBasetype(); + const bool needsDestruction = + (!isConstructing && elemType->needsDestruction()); + const bool needsPostblit = + (op != TOKblit && !canSkipPostblit && arrayNeedsPostblit(t)); + + LLValue *realLhsPtr = DtoArrayPtr(lhs); + LLValue *lhsPtr = DtoBitCast(realLhsPtr, getVoidPtrType()); + LLValue *lhsLength = DtoArrayLen(lhs); + + // Be careful to handle void arrays correctly when modifying this (see tests + // for DMD issue 7493). + // TODO: This should use AssignExp::ismemset. + LLValue *realRhsArrayPtr = + (t2->ty == Tarray || t2->ty == Tsarray ? DtoArrayPtr(rhs) : NULL); + if (realRhsArrayPtr && realRhsArrayPtr->getType() == realLhsPtr->getType()) { + // T[] = T[] T[] = T[n] + // T[n] = T[n] T[n] = T[] + LLValue *rhsPtr = DtoBitCast(realRhsArrayPtr, getVoidPtrType()); + LLValue *rhsLength = DtoArrayLen(rhs); + + if (!needsDestruction && !needsPostblit) { + // fast version + LLValue *elemSize = + DtoConstSize_t(getTypePaddedSize(DtoMemType(elemType))); + LLValue *lhsSize = gIR->ir->CreateMul(elemSize, lhsLength); + + if (rhs->isNull()) { + DtoMemSetZero(lhsPtr, lhsSize); + } else { + LLValue *rhsSize = gIR->ir->CreateMul(elemSize, rhsLength); + const bool knownInBounds = + isConstructing || (t->ty == Tsarray && t2->ty == Tsarray); + copySlice(loc, lhsPtr, lhsSize, rhsPtr, rhsSize, knownInBounds); + } + } else if (isConstructing) { + LLFunction *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arrayctor"); + LLCallSite call = gIR->CreateCallOrInvoke(fn, DtoTypeInfoOf(elemType), + DtoSlice(rhsPtr, rhsLength), + DtoSlice(lhsPtr, lhsLength)); + call.setCallingConv(llvm::CallingConv::C); + } else // assigning { - // scalar rhs: - // T[] = T T[n][] = T - // T[n] = T T[n][m] = T - if (!needsDestruction && !needsPostblit) - { - // fast version - LLValue* elemSize = DtoConstSize_t(getTypePaddedSize(realLhsPtr->getType()->getContainedType(0))); - LLValue* lhsSize = gIR->ir->CreateMul(elemSize, lhsLength); - LLType* rhsType = DtoMemType(t2); - LLValue* rhsSize = DtoConstSize_t(getTypePaddedSize(rhsType)); - LLValue* actualPtr = DtoBitCast(lhsPtr, rhsType->getPointerTo()); - LLValue* actualLength = gIR->ir->CreateExactUDiv(lhsSize, rhsSize); - DtoArrayInit(loc, actualPtr, actualLength, rhs, op); - } - else - { - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, - isConstructing ? "_d_arraysetctor" : "_d_arraysetassign"); - LLCallSite call = gIR->CreateCallOrInvoke( - fn, - lhsPtr, - DtoBitCast(makeLValue(loc, rhs), getVoidPtrType()), - gIR->ir->CreateTruncOrBitCast(lhsLength, LLType::getInt32Ty(gIR->context())), - DtoTypeInfoOf(stripModifiers(t2)) - ); - call.setCallingConv(llvm::CallingConv::C); - } + LLValue *tmpSwap = DtoAlloca(elemType, "arrayAssign.tmpSwap"); + LLFunction *fn = LLVM_D_GetRuntimeFunction( + loc, gIR->module, + !canSkipPostblit ? "_d_arrayassign_l" : "_d_arrayassign_r"); + LLCallSite call = gIR->CreateCallOrInvoke( + fn, DtoTypeInfoOf(elemType), DtoSlice(rhsPtr, rhsLength), + DtoSlice(lhsPtr, lhsLength), DtoBitCast(tmpSwap, getVoidPtrType())); + call.setCallingConv(llvm::CallingConv::C); } + } else { + // scalar rhs: + // T[] = T T[n][] = T + // T[n] = T T[n][m] = T + if (!needsDestruction && !needsPostblit) { + // fast version + LLValue *elemSize = DtoConstSize_t( + getTypePaddedSize(realLhsPtr->getType()->getContainedType(0))); + LLValue *lhsSize = gIR->ir->CreateMul(elemSize, lhsLength); + LLType *rhsType = DtoMemType(t2); + LLValue *rhsSize = DtoConstSize_t(getTypePaddedSize(rhsType)); + LLValue *actualPtr = DtoBitCast(lhsPtr, rhsType->getPointerTo()); + LLValue *actualLength = gIR->ir->CreateExactUDiv(lhsSize, rhsSize); + DtoArrayInit(loc, actualPtr, actualLength, rhs, op); + } else { + LLFunction *fn = LLVM_D_GetRuntimeFunction( + loc, gIR->module, + isConstructing ? "_d_arraysetctor" : "_d_arraysetassign"); + LLCallSite call = gIR->CreateCallOrInvoke( + fn, lhsPtr, DtoBitCast(makeLValue(loc, rhs), getVoidPtrType()), + gIR->ir->CreateTruncOrBitCast(lhsLength, + LLType::getInt32Ty(gIR->context())), + DtoTypeInfoOf(stripModifiers(t2))); + call.setCallingConv(llvm::CallingConv::C); + } + } } ////////////////////////////////////////////////////////////////////////////////////////// -static void DtoSetArray(DValue* array, LLValue* dim, LLValue* ptr) -{ - IF_LOG Logger::println("SetArray"); - LLValue *arr = array->getLVal(); - assert(isaStruct(arr->getType()->getContainedType(0))); - DtoStore(dim, DtoGEPi(arr,0,0)); - DtoStore(ptr, DtoGEPi(arr,0,1)); +static void DtoSetArray(DValue *array, LLValue *dim, LLValue *ptr) { + IF_LOG Logger::println("SetArray"); + LLValue *arr = array->getLVal(); + assert(isaStruct(arr->getType()->getContainedType(0))); + DtoStore(dim, DtoGEPi(arr, 0, 0)); + DtoStore(ptr, DtoGEPi(arr, 0, 1)); } ////////////////////////////////////////////////////////////////////////////////////////// -LLConstant* DtoConstArrayInitializer(ArrayInitializer* arrinit, Type* targetType) -{ - IF_LOG Logger::println("DtoConstArrayInitializer: %s | %s", arrinit->toChars(), targetType->toChars()); - LOG_SCOPE; +LLConstant *DtoConstArrayInitializer(ArrayInitializer *arrinit, + Type *targetType) { + IF_LOG Logger::println("DtoConstArrayInitializer: %s | %s", + arrinit->toChars(), targetType->toChars()); + LOG_SCOPE; - assert(arrinit->value.dim == arrinit->index.dim); + assert(arrinit->value.dim == arrinit->index.dim); - // get base array type - Type* arrty = targetType->toBasetype(); - size_t arrlen = arrinit->dim; + // get base array type + Type *arrty = targetType->toBasetype(); + size_t arrlen = arrinit->dim; - // for statis arrays, dmd does not include any trailing default - // initialized elements in the value/index lists - if (arrty->ty == Tsarray) - { - TypeSArray* tsa = static_cast(arrty); - arrlen = static_cast(tsa->dim->toInteger()); + // for statis arrays, dmd does not include any trailing default + // initialized elements in the value/index lists + if (arrty->ty == Tsarray) { + TypeSArray *tsa = static_cast(arrty); + arrlen = static_cast(tsa->dim->toInteger()); + } + + // make sure the number of initializers is sane + if (arrinit->index.dim > arrlen || arrinit->dim > arrlen) { + error(arrinit->loc, "too many initializers, %llu, for array[%llu]", + static_cast(arrinit->index.dim), + static_cast(arrlen)); + fatal(); + } + + // get elem type + Type *elemty; + if (arrty->ty == Tvector) + elemty = static_cast(arrty)->elementType(); + else + elemty = arrty->nextOf(); + LLType *llelemty = DtoMemType(elemty); + + // true if array elements differ in type, can happen with array of unions + bool mismatch = false; + + // allocate room for initializers + std::vector initvals(arrlen, NULL); + + // go through each initializer, they're not sorted by index by the frontend + size_t j = 0; + for (size_t i = 0; i < arrinit->index.dim; i++) { + // get index + Expression *idx = static_cast(arrinit->index.data[i]); + + // idx can be null, then it's just the next element + if (idx) + j = idx->toInteger(); + assert(j < arrlen); + + // get value + Initializer *val = static_cast(arrinit->value.data[i]); + assert(val); + + // error check from dmd + if (initvals[j] != NULL) { + error(arrinit->loc, "duplicate initialization for index %llu", + static_cast(j)); } - // make sure the number of initializers is sane - if (arrinit->index.dim > arrlen || arrinit->dim > arrlen) - { - error(arrinit->loc, "too many initializers, %llu, for array[%llu]", - static_cast(arrinit->index.dim), - static_cast(arrlen)); - fatal(); + LLConstant *c = DtoConstInitializer(val->loc, elemty, val); + assert(c); + if (c->getType() != llelemty) + mismatch = true; + + initvals[j] = c; + j++; + } + + // die now if there was errors + if (global.errors) + fatal(); + + // Fill out any null entries still left with default values. + + // Element default initializer. Compute lazily to be able to avoid infinite + // recursion for types with members that are default initialized to empty + // arrays of themselves. + LLConstant *elemDefaultInit = NULL; + for (size_t i = 0; i < arrlen; i++) { + if (initvals[i] != NULL) + continue; + + if (!elemDefaultInit) { + elemDefaultInit = DtoConstExpInit(arrinit->loc, elemty, + elemty->defaultInit(arrinit->loc)); + if (elemDefaultInit->getType() != llelemty) { + mismatch = true; + } } - // get elem type - Type* elemty; + initvals[i] = elemDefaultInit; + } + + LLConstant *constarr; + if (mismatch) + constarr = LLConstantStruct::getAnon(gIR->context(), + initvals); // FIXME should this pack? + else { if (arrty->ty == Tvector) - elemty = static_cast(arrty)->elementType(); + constarr = llvm::ConstantVector::get(initvals); else - elemty = arrty->nextOf(); - LLType* llelemty = DtoMemType(elemty); + constarr = + LLConstantArray::get(LLArrayType::get(llelemty, arrlen), initvals); + } - // true if array elements differ in type, can happen with array of unions - bool mismatch = false; + // std::cout << "constarr: " << *constarr << std::endl; - // allocate room for initializers - std::vector initvals(arrlen, NULL); + // if the type is a static array, we're done + if (arrty->ty == Tsarray || arrty->ty == Tvector) + return constarr; - // go through each initializer, they're not sorted by index by the frontend - size_t j = 0; - for (size_t i = 0; i < arrinit->index.dim; i++) - { - // get index - Expression* idx = static_cast(arrinit->index.data[i]); + // we need to make a global with the data, so we have a pointer to the array + // Important: don't make the gvar constant, since this const initializer might + // be used as an initializer for a static T[] - where modifying contents is + // allowed. + LLGlobalVariable *gvar = new LLGlobalVariable( + gIR->module, constarr->getType(), false, LLGlobalValue::InternalLinkage, + constarr, ".constarray"); - // idx can be null, then it's just the next element - if (idx) - j = idx->toInteger(); - assert(j < arrlen); + if (arrty->ty == Tpointer) + // we need to return pointer to the static array. + return DtoBitCast(gvar, DtoType(arrty)); - // get value - Initializer* val = static_cast(arrinit->value.data[i]); - assert(val); - - // error check from dmd - if (initvals[j] != NULL) - { - error(arrinit->loc, "duplicate initialization for index %llu", static_cast(j)); - } - - LLConstant* c = DtoConstInitializer(val->loc, elemty, val); - assert(c); - if (c->getType() != llelemty) - mismatch = true; - - initvals[j] = c; - j++; - } - - // die now if there was errors - if (global.errors) - fatal(); - - // Fill out any null entries still left with default values. - - // Element default initializer. Compute lazily to be able to avoid infinite - // recursion for types with members that are default initialized to empty - // arrays of themselves. - LLConstant* elemDefaultInit = NULL; - for (size_t i = 0; i < arrlen; i++) - { - if (initvals[i] != NULL) - continue; - - if (!elemDefaultInit) - { - elemDefaultInit = DtoConstExpInit(arrinit->loc, elemty, - elemty->defaultInit(arrinit->loc)); - if (elemDefaultInit->getType() != llelemty) - { - mismatch = true; - } - } - - initvals[i] = elemDefaultInit; - } - - LLConstant* constarr; - if (mismatch) - constarr = LLConstantStruct::getAnon(gIR->context(), initvals); // FIXME should this pack? - else - { - if (arrty->ty == Tvector) - constarr = llvm::ConstantVector::get(initvals); - else - constarr = LLConstantArray::get(LLArrayType::get(llelemty, arrlen), initvals); - } - -// std::cout << "constarr: " << *constarr << std::endl; - - // if the type is a static array, we're done - if (arrty->ty == Tsarray || arrty->ty == Tvector) - return constarr; - - // we need to make a global with the data, so we have a pointer to the array - // Important: don't make the gvar constant, since this const initializer might - // be used as an initializer for a static T[] - where modifying contents is allowed. - LLGlobalVariable* gvar = new LLGlobalVariable(gIR->module, constarr->getType(), false, LLGlobalValue::InternalLinkage, constarr, ".constarray"); - - if (arrty->ty == Tpointer) - // we need to return pointer to the static array. - return DtoBitCast(gvar, DtoType(arrty)); - - LLConstant* idxs[2] = { DtoConstUint(0), DtoConstUint(0) }; + LLConstant *idxs[2] = {DtoConstUint(0), DtoConstUint(0)}; #if LDC_LLVM_VER >= 307 - LLConstant* gep = llvm::ConstantExpr::getGetElementPtr(isaPointer(gvar)->getElementType(), gvar, idxs, true); + LLConstant *gep = llvm::ConstantExpr::getGetElementPtr( + isaPointer(gvar)->getElementType(), gvar, idxs, true); #else - LLConstant* gep = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true); + LLConstant *gep = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true); #endif - gep = llvm::ConstantExpr::getBitCast(gvar, getPtrToType(llelemty)); + gep = llvm::ConstantExpr::getBitCast(gvar, getPtrToType(llelemty)); - return DtoConstSlice(DtoConstSize_t(arrlen), gep, arrty); + return DtoConstSlice(DtoConstSize_t(arrlen), gep, arrty); } ////////////////////////////////////////////////////////////////////////////////////////// -bool isConstLiteral(ArrayLiteralExp* ale) -{ - // FIXME: This is overly pessemistic, isConst() always returns 0 e.g. for - // StructLiteralExps. Thus, we waste optimization potential (GitHub #506). - for (size_t i = 0; i < ale->elements->dim; ++i) - { - // We have to check specifically for '1', as SymOffExp is classified as - // '2' and the address of a local variable is not an LLVM constant. - if ((*ale->elements)[i]->isConst() != 1) - return false; - } - return true; +bool isConstLiteral(ArrayLiteralExp *ale) { + // FIXME: This is overly pessemistic, isConst() always returns 0 e.g. for + // StructLiteralExps. Thus, we waste optimization potential (GitHub #506). + for (size_t i = 0; i < ale->elements->dim; ++i) { + // We have to check specifically for '1', as SymOffExp is classified as + // '2' and the address of a local variable is not an LLVM constant. + if ((*ale->elements)[i]->isConst() != 1) + return false; + } + return true; } ////////////////////////////////////////////////////////////////////////////////////////// -llvm::Constant* arrayLiteralToConst(IRState* p, ArrayLiteralExp* ale) -{ - // Build the initializer. We have to take care as due to unions in the - // element types (with different fields being initialized), we can end up - // with different types for the initializer values. In this case, we - // generate a packed struct constant instead of an array constant. - LLType *elementType = NULL; - bool differentTypes = false; - - std::vector vals; - vals.reserve(ale->elements->dim); - for (unsigned i = 0; i < ale->elements->dim; ++i) - { - llvm::Constant *val = toConstElem((*ale->elements)[i], p); - if (!elementType) - elementType = val->getType(); - else - differentTypes |= (elementType != val->getType()); - vals.push_back(val); - } - - if (differentTypes) - return llvm::ConstantStruct::getAnon(vals, true); +llvm::Constant *arrayLiteralToConst(IRState *p, ArrayLiteralExp *ale) { + // Build the initializer. We have to take care as due to unions in the + // element types (with different fields being initialized), we can end up + // with different types for the initializer values. In this case, we + // generate a packed struct constant instead of an array constant. + LLType *elementType = NULL; + bool differentTypes = false; + std::vector vals; + vals.reserve(ale->elements->dim); + for (unsigned i = 0; i < ale->elements->dim; ++i) { + llvm::Constant *val = toConstElem((*ale->elements)[i], p); if (!elementType) - { - assert(ale->elements->dim == 0); - elementType = DtoMemType(ale->type->toBasetype()->nextOf()); - return llvm::ConstantArray::get(LLArrayType::get(elementType, 0), vals); - } - - llvm::ArrayType *t = llvm::ArrayType::get(elementType, ale->elements->dim); - return llvm::ConstantArray::get(t, vals); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem) -{ - size_t elemCount = ale->elements->dim; - - // Don't try to write nothing to a zero-element array, we might represent it - // as a null pointer. - if (elemCount == 0) return; - - if (isConstLiteral(ale)) - { - llvm::Constant* constarr = arrayLiteralToConst(p, ale); - - // Emit a global for longer arrays, as an inline constant is always - // lowered to a series of movs or similar at the asm level. The - // optimizer can still decide to promote the memcpy intrinsic, so - // the cutoff merely affects compilation speed. - if (elemCount <= 4) - { - DtoStore(constarr, DtoBitCast(dstMem, getPtrToType(constarr->getType()))); - } - else - { - llvm::GlobalVariable* gvar = new llvm::GlobalVariable( - gIR->module, - constarr->getType(), - true, - LLGlobalValue::InternalLinkage, - constarr, - ".arrayliteral" - ); - gvar->setUnnamedAddr(true); - DtoMemCpy(dstMem, gvar, DtoConstSize_t(getTypePaddedSize(constarr->getType()))); - } - } + elementType = val->getType(); else - { - // Store the elements one by one. - for (size_t i = 0; i < elemCount; ++i) - { - DValue* e = toElem((*ale->elements)[i]); + differentTypes |= (elementType != val->getType()); + vals.push_back(val); + } - LLValue* elemAddr = DtoGEPi(dstMem, 0, i, "", p->scopebb()); - DVarValue* vv = new DVarValue(e->type, elemAddr); - DtoAssign(ale->loc, vv, e, TOKconstruct, true); - } + if (differentTypes) + return llvm::ConstantStruct::getAnon(vals, true); + + if (!elementType) { + assert(ale->elements->dim == 0); + elementType = DtoMemType(ale->type->toBasetype()->nextOf()); + return llvm::ConstantArray::get(LLArrayType::get(elementType, 0), vals); + } + + llvm::ArrayType *t = llvm::ArrayType::get(elementType, ale->elements->dim); + return llvm::ConstantArray::get(t, vals); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void initializeArrayLiteral(IRState *p, ArrayLiteralExp *ale, LLValue *dstMem) { + size_t elemCount = ale->elements->dim; + + // Don't try to write nothing to a zero-element array, we might represent it + // as a null pointer. + if (elemCount == 0) + return; + + if (isConstLiteral(ale)) { + llvm::Constant *constarr = arrayLiteralToConst(p, ale); + + // Emit a global for longer arrays, as an inline constant is always + // lowered to a series of movs or similar at the asm level. The + // optimizer can still decide to promote the memcpy intrinsic, so + // the cutoff merely affects compilation speed. + if (elemCount <= 4) { + DtoStore(constarr, DtoBitCast(dstMem, getPtrToType(constarr->getType()))); + } else { + llvm::GlobalVariable *gvar = new llvm::GlobalVariable( + gIR->module, constarr->getType(), true, + LLGlobalValue::InternalLinkage, constarr, ".arrayliteral"); + gvar->setUnnamedAddr(true); + DtoMemCpy(dstMem, gvar, + DtoConstSize_t(getTypePaddedSize(constarr->getType()))); } + } else { + // Store the elements one by one. + for (size_t i = 0; i < elemCount; ++i) { + DValue *e = toElem((*ale->elements)[i]); + + LLValue *elemAddr = DtoGEPi(dstMem, 0, i, "", p->scopebb()); + DVarValue *vv = new DVarValue(e->type, elemAddr); + DtoAssign(ale->loc, vv, e, TOKconstruct, true); + } + } } ////////////////////////////////////////////////////////////////////////////////////////// -LLConstant* DtoConstSlice(LLConstant* dim, LLConstant* ptr, Type *type) -{ - LLConstant* values[2] = { dim, ptr }; - llvm::ArrayRef valuesRef = llvm::makeArrayRef(values, 2); - LLStructType *lltype = type ? - isaStruct(DtoType(type)) : - LLConstantStruct::getTypeForElements(gIR->context(), valuesRef); - return LLConstantStruct::get(lltype, valuesRef); +LLConstant *DtoConstSlice(LLConstant *dim, LLConstant *ptr, Type *type) { + LLConstant *values[2] = {dim, ptr}; + llvm::ArrayRef valuesRef = llvm::makeArrayRef(values, 2); + LLStructType *lltype = + type ? isaStruct(DtoType(type)) + : LLConstantStruct::getTypeForElements(gIR->context(), valuesRef); + return LLConstantStruct::get(lltype, valuesRef); } ////////////////////////////////////////////////////////////////////////////////////////// -static DSliceValue *getSlice(Type *arrayType, LLValue *array) -{ - // Get ptr and length of the array - LLValue* arrayLen = DtoExtractValue(array, 0, ".len"); - LLValue* newptr = DtoExtractValue(array, 1, ".ptr"); +static DSliceValue *getSlice(Type *arrayType, LLValue *array) { + // Get ptr and length of the array + LLValue *arrayLen = DtoExtractValue(array, 0, ".len"); + LLValue *newptr = DtoExtractValue(array, 1, ".ptr"); - // cast pointer to wanted type - LLType* dstType = DtoType(arrayType)->getContainedType(1); - if (newptr->getType() != dstType) - newptr = DtoBitCast(newptr, dstType, ".gc_mem"); + // cast pointer to wanted type + LLType *dstType = DtoType(arrayType)->getContainedType(1); + if (newptr->getType() != dstType) + newptr = DtoBitCast(newptr, dstType, ".gc_mem"); - return new DSliceValue(arrayType, arrayLen, newptr); + return new DSliceValue(arrayType, arrayLen, newptr); } ////////////////////////////////////////////////////////////////////////////////////////// -DSliceValue* DtoNewDynArray(Loc& loc, Type* arrayType, DValue* dim, bool defaultInit) -{ - IF_LOG Logger::println("DtoNewDynArray : %s", arrayType->toChars()); - LOG_SCOPE; +DSliceValue *DtoNewDynArray(Loc &loc, Type *arrayType, DValue *dim, + bool defaultInit) { + IF_LOG Logger::println("DtoNewDynArray : %s", arrayType->toChars()); + LOG_SCOPE; - // typeinfo arg - LLValue* arrayTypeInfo = DtoTypeInfoOf(arrayType); + // typeinfo arg + LLValue *arrayTypeInfo = DtoTypeInfoOf(arrayType); - // dim arg - assert(DtoType(dim->getType()) == DtoSize_t()); - LLValue* arrayLen = dim->getRVal(); + // dim arg + assert(DtoType(dim->getType()) == DtoSize_t()); + LLValue *arrayLen = dim->getRVal(); - // get runtime function - Type* eltType = arrayType->toBasetype()->nextOf(); - bool zeroInit = eltType->isZeroInit(); + // get runtime function + Type *eltType = arrayType->toBasetype()->nextOf(); + bool zeroInit = eltType->isZeroInit(); - const char* fnname = defaultInit ? - (zeroInit ? "_d_newarrayT" : "_d_newarrayiT") : - "_d_newarrayU"; - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, fnname); + const char *fnname = defaultInit + ? (zeroInit ? "_d_newarrayT" : "_d_newarrayiT") + : "_d_newarrayU"; + LLFunction *fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, fnname); - // call allocator - LLValue* newArray = gIR->CreateCallOrInvoke(fn, arrayTypeInfo, arrayLen, ".gc_mem").getInstruction(); + // call allocator + LLValue *newArray = + gIR->CreateCallOrInvoke(fn, arrayTypeInfo, arrayLen, ".gc_mem") + .getInstruction(); - return getSlice(arrayType, newArray); + return getSlice(arrayType, newArray); } ////////////////////////////////////////////////////////////////////////////////////////// -DSliceValue* DtoNewMulDimDynArray(Loc& loc, Type* arrayType, DValue** dims, size_t ndims) -{ - IF_LOG Logger::println("DtoNewMulDimDynArray : %s", arrayType->toChars()); - LOG_SCOPE; +DSliceValue *DtoNewMulDimDynArray(Loc &loc, Type *arrayType, DValue **dims, + size_t ndims) { + IF_LOG Logger::println("DtoNewMulDimDynArray : %s", arrayType->toChars()); + LOG_SCOPE; - // typeinfo arg - LLValue* arrayTypeInfo = DtoTypeInfoOf(arrayType); + // typeinfo arg + LLValue *arrayTypeInfo = DtoTypeInfoOf(arrayType); - // get value type - Type* vtype = arrayType->toBasetype(); + // get value type + Type *vtype = arrayType->toBasetype(); + for (size_t i = 0; i < ndims; ++i) + vtype = vtype->nextOf(); + + // get runtime function + const char *fnname = + vtype->isZeroInit() ? "_d_newarraymTX" : "_d_newarraymiTX"; + LLFunction *fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, fnname); + + // Check if constant + bool allDimsConst = true; + for (size_t i = 0; i < ndims; ++i) { + if (!isaConstant(dims[i]->getRVal())) + allDimsConst = false; + } + + // build dims + LLValue *array; + if (allDimsConst) { + // Build constant array for dimensions + std::vector argsdims; + argsdims.reserve(ndims); + for (size_t i = 0; i < ndims; ++i) { + argsdims.push_back(isaConstant(dims[i]->getRVal())); + } + + llvm::Constant *dims = llvm::ConstantArray::get( + llvm::ArrayType::get(DtoSize_t(), ndims), argsdims); + LLGlobalVariable *gvar = new llvm::GlobalVariable( + gIR->module, dims->getType(), true, LLGlobalValue::InternalLinkage, + dims, ".dimsarray"); + array = llvm::ConstantExpr::getBitCast(gvar, getPtrToType(dims->getType())); + } else { + // Build static array for dimensions + LLArrayType *type = LLArrayType::get(DtoSize_t(), ndims); + array = DtoRawAlloca(type, 0, ".dimarray"); for (size_t i = 0; i < ndims; ++i) - vtype = vtype->nextOf(); + DtoStore(dims[i]->getRVal(), DtoGEPi(array, 0, i, ".ndim")); + } - // get runtime function - const char* fnname = vtype->isZeroInit() ? "_d_newarraymTX" : "_d_newarraymiTX"; - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, fnname); + LLStructType *dtype = DtoArrayType(DtoSize_t()); + LLValue *darray = DtoRawAlloca(dtype, 0, ".array"); + DtoStore(DtoConstSize_t(ndims), DtoGEPi(darray, 0, 0, ".len")); + DtoStore(DtoBitCast(array, getPtrToType(DtoSize_t())), + DtoGEPi(darray, 0, 1, ".ptr")); - // Check if constant - bool allDimsConst = true; - for (size_t i = 0; i < ndims; ++i) - { - if (!isaConstant(dims[i]->getRVal())) - allDimsConst = false; + // call allocator + LLValue *newptr = + gIR->CreateCallOrInvoke(fn, arrayTypeInfo, DtoLoad(darray), ".gc_mem") + .getInstruction(); + + IF_LOG Logger::cout() << "final ptr = " << *newptr << '\n'; + + return getSlice(arrayType, newptr); +} + +////////////////////////////////////////////////////////////////////////////////////////// +DSliceValue *DtoResizeDynArray(Loc &loc, Type *arrayType, DValue *array, + LLValue *newdim) { + IF_LOG Logger::println("DtoResizeDynArray : %s", arrayType->toChars()); + LOG_SCOPE; + + assert(array); + assert(newdim); + assert(arrayType); + assert(arrayType->toBasetype()->ty == Tarray); + + // decide on what runtime function to call based on whether the type is zero + // initialized + bool zeroInit = arrayType->toBasetype()->nextOf()->isZeroInit(); + + // call runtime + LLFunction *fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, + zeroInit ? "_d_arraysetlengthT" + : "_d_arraysetlengthiT"); + + LLValue *newArray = gIR->CreateCallOrInvoke( + fn, DtoTypeInfoOf(arrayType), newdim, + DtoBitCast(array->getLVal(), + fn->getFunctionType()->getParamType(2)), + ".gc_mem") + .getInstruction(); + + return getSlice(arrayType, newArray); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void DtoCatAssignElement(Loc &loc, Type *arrayType, DValue *array, + Expression *exp) { + IF_LOG Logger::println("DtoCatAssignElement"); + LOG_SCOPE; + + assert(array); + + LLValue *oldLength = DtoArrayLen(array); + + // Do not move exp->toElem call after creating _d_arrayappendcTX, + // otherwise a ~= a[$-i] won't work correctly + DValue *expVal = toElem(exp); + + LLFunction *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arrayappendcTX"); + LLValue *appendedArray = + gIR->CreateCallOrInvoke( + fn, DtoTypeInfoOf(arrayType), + DtoBitCast(array->getLVal(), + fn->getFunctionType()->getParamType(1)), + DtoConstSize_t(1), ".appendedArray") + .getInstruction(); + appendedArray = DtoAggrPaint(appendedArray, DtoType(arrayType)); + + LLValue *val = DtoArrayPtr(array); + val = DtoGEP1(val, oldLength, ".lastElem"); + DtoAssign(loc, new DVarValue(arrayType->nextOf(), val), expVal, TOKblit); + callPostblit(loc, exp, val); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DSliceValue *DtoCatAssignArray(Loc &loc, DValue *arr, Expression *exp) { + IF_LOG Logger::println("DtoCatAssignArray"); + LOG_SCOPE; + Type *arrayType = arr->getType(); + + LLFunction *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arrayappendT"); + // Call _d_arrayappendT(TypeInfo ti, byte[] *px, byte[] y) + LLValue *newArray = + gIR->CreateCallOrInvoke( + fn, DtoTypeInfoOf(arrayType), + DtoBitCast(arr->getLVal(), fn->getFunctionType()->getParamType(1)), + DtoAggrPaint(DtoSlice(toElem(exp)), + fn->getFunctionType()->getParamType(2)), + ".appendedArray") + .getInstruction(); + + return getSlice(arrayType, newArray); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DSliceValue *DtoCatArrays(Loc &loc, Type *arrayType, Expression *exp1, + Expression *exp2) { + IF_LOG Logger::println("DtoCatAssignArray"); + LOG_SCOPE; + + llvm::SmallVector args; + LLFunction *fn = 0; + + if (exp1->op == TOKcat) { // handle multiple concat + fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arraycatnTX"); + + // Create array of slices + typedef llvm::SmallVector ArgVector; + ArgVector arrs; + arrs.push_back(DtoSlicePtr(toElem(exp2))); + CatExp *ce = static_cast(exp1); + do { + arrs.push_back(DtoSlicePtr(toElem(ce->e2))); + ce = static_cast(ce->e1); + } while (ce->op == TOKcat); + arrs.push_back(DtoSlicePtr(toElem(ce))); + + // Create static array from slices + LLPointerType *ptrarraytype = isaPointer(arrs[0]->getType()); + assert(ptrarraytype && "Expected pointer type"); + LLStructType *arraytype = isaStruct(ptrarraytype->getElementType()); + assert(arraytype && "Expected struct type"); + LLArrayType *type = LLArrayType::get(arraytype, arrs.size()); + LLValue *array = DtoRawAlloca(type, 0, ".slicearray"); + unsigned int i = 0; + for (ArgVector::reverse_iterator I = arrs.rbegin(), E = arrs.rend(); I != E; + ++I) { + LLValue *v = DtoLoad(DtoBitCast(*I, ptrarraytype)); + DtoStore(v, DtoGEPi(array, 0, i++, ".slice")); } - // build dims - LLValue* array; - if (allDimsConst) - { - // Build constant array for dimensions - std::vector argsdims; - argsdims.reserve(ndims); - for (size_t i = 0; i < ndims; ++i) - { - argsdims.push_back(isaConstant(dims[i]->getRVal())); - } + LLStructType *type2 = DtoArrayType(arraytype); + LLValue *array2 = DtoRawAlloca(type2, 0, ".array"); + DtoStore(DtoConstSize_t(arrs.size()), DtoGEPi(array2, 0, 0, ".len")); + DtoStore(DtoBitCast(array, ptrarraytype), DtoGEPi(array2, 0, 1, ".ptr")); + LLValue *val = + DtoLoad(DtoBitCast(array2, getPtrToType(DtoArrayType(DtoArrayType( + LLType::getInt8Ty(gIR->context())))))); - llvm::Constant* dims = llvm::ConstantArray::get(llvm::ArrayType::get(DtoSize_t(), ndims), argsdims); - LLGlobalVariable* gvar = new llvm::GlobalVariable(gIR->module, dims->getType(), true, LLGlobalValue::InternalLinkage, dims, ".dimsarray"); - array = llvm::ConstantExpr::getBitCast(gvar, getPtrToType(dims->getType())); - } - else - { - // Build static array for dimensions - LLArrayType* type = LLArrayType::get(DtoSize_t(), ndims); - array = DtoRawAlloca(type, 0, ".dimarray"); - for (size_t i = 0; i < ndims; ++i) - DtoStore(dims[i]->getRVal(), DtoGEPi(array, 0, i, ".ndim")); - } + // TypeInfo ti + args.push_back(DtoTypeInfoOf(arrayType)); + // byte[][] arrs + args.push_back(val); + } else { + fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arraycatT"); - LLStructType* dtype = DtoArrayType(DtoSize_t()); - LLValue* darray = DtoRawAlloca(dtype, 0, ".array"); - DtoStore(DtoConstSize_t(ndims), DtoGEPi(darray, 0, 0, ".len")); - DtoStore(DtoBitCast(array, getPtrToType(DtoSize_t())), DtoGEPi(darray, 0, 1, ".ptr")); + // TypeInfo ti + args.push_back(DtoTypeInfoOf(arrayType)); + // byte[] x + LLValue *val = DtoLoad(DtoSlicePtr(toElem(exp1))); + val = DtoAggrPaint(val, fn->getFunctionType()->getParamType(1)); + args.push_back(val); + // byte[] y + val = DtoLoad(DtoSlicePtr(toElem(exp2))); + val = DtoAggrPaint(val, fn->getFunctionType()->getParamType(2)); + args.push_back(val); + } - // call allocator - LLValue* newptr = gIR->CreateCallOrInvoke(fn, - arrayTypeInfo, - DtoLoad(darray), - ".gc_mem" - ).getInstruction(); - - IF_LOG Logger::cout() << "final ptr = " << *newptr << '\n'; - - return getSlice(arrayType, newptr); -} - -////////////////////////////////////////////////////////////////////////////////////////// -DSliceValue* DtoResizeDynArray(Loc& loc, Type* arrayType, DValue* array, LLValue* newdim) -{ - IF_LOG Logger::println("DtoResizeDynArray : %s", arrayType->toChars()); - LOG_SCOPE; - - assert(array); - assert(newdim); - assert(arrayType); - assert(arrayType->toBasetype()->ty == Tarray); - - // decide on what runtime function to call based on whether the type is zero initialized - bool zeroInit = arrayType->toBasetype()->nextOf()->isZeroInit(); - - // call runtime - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, zeroInit ? "_d_arraysetlengthT" : "_d_arraysetlengthiT" ); - - LLValue* newArray = gIR->CreateCallOrInvoke( - fn, - DtoTypeInfoOf(arrayType), - newdim, - DtoBitCast(array->getLVal(), fn->getFunctionType()->getParamType(2)), - ".gc_mem") - .getInstruction(); - - return getSlice(arrayType, newArray); + LLValue *newArray = gIR->func() + ->scopes->callOrInvoke(fn, args, ".appendedArray") + .getInstruction(); + return getSlice(arrayType, newArray); } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoCatAssignElement(Loc& loc, Type* arrayType, DValue* array, Expression* exp) -{ - IF_LOG Logger::println("DtoCatAssignElement"); - LOG_SCOPE; +DSliceValue *DtoAppendDChar(Loc &loc, DValue *arr, Expression *exp, + const char *func) { + Type *arrayType = arr->getType(); + DValue *valueToAppend = toElem(exp); - assert(array); + // Prepare arguments + LLFunction *fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, func); - LLValue *oldLength = DtoArrayLen(array); + // Call function (ref string x, dchar c) + LLValue *newArray = + gIR->CreateCallOrInvoke( + fn, + DtoBitCast(arr->getLVal(), fn->getFunctionType()->getParamType(0)), + DtoBitCast(valueToAppend->getRVal(), + fn->getFunctionType()->getParamType(1)), + ".appendedArray") + .getInstruction(); - // Do not move exp->toElem call after creating _d_arrayappendcTX, - // otherwise a ~= a[$-i] won't work correctly - DValue *expVal = toElem(exp); - - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arrayappendcTX"); - LLValue* appendedArray = gIR->CreateCallOrInvoke( - fn, - DtoTypeInfoOf(arrayType), - DtoBitCast(array->getLVal(), fn->getFunctionType()->getParamType(1)), - DtoConstSize_t(1), - ".appendedArray" - ).getInstruction(); - appendedArray = DtoAggrPaint(appendedArray, DtoType(arrayType)); - - LLValue* val = DtoArrayPtr(array); - val = DtoGEP1(val, oldLength, ".lastElem"); - DtoAssign(loc, new DVarValue(arrayType->nextOf(), val), expVal, TOKblit); - callPostblit(loc, exp, val); + return getSlice(arrayType, newArray); } ////////////////////////////////////////////////////////////////////////////////////////// -DSliceValue* DtoCatAssignArray(Loc& loc, DValue* arr, Expression* exp) -{ - IF_LOG Logger::println("DtoCatAssignArray"); - LOG_SCOPE; - Type *arrayType = arr->getType(); - - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arrayappendT"); - // Call _d_arrayappendT(TypeInfo ti, byte[] *px, byte[] y) - LLValue* newArray = gIR->CreateCallOrInvoke( - fn, - DtoTypeInfoOf(arrayType), - DtoBitCast(arr->getLVal(), fn->getFunctionType()->getParamType(1)), - DtoAggrPaint(DtoSlice(toElem(exp)), fn->getFunctionType()->getParamType(2)), - ".appendedArray" - ).getInstruction(); - - return getSlice(arrayType, newArray); +DSliceValue *DtoAppendDCharToString(Loc &loc, DValue *arr, Expression *exp) { + IF_LOG Logger::println("DtoAppendDCharToString"); + LOG_SCOPE; + return DtoAppendDChar(loc, arr, exp, "_d_arrayappendcd"); } ////////////////////////////////////////////////////////////////////////////////////////// -DSliceValue* DtoCatArrays(Loc& loc, Type* arrayType, Expression* exp1, Expression* exp2) -{ - IF_LOG Logger::println("DtoCatAssignArray"); - LOG_SCOPE; - - llvm::SmallVector args; - LLFunction* fn = 0; - - if (exp1->op == TOKcat) - { // handle multiple concat - fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arraycatnTX"); - - // Create array of slices - typedef llvm::SmallVector ArgVector; - ArgVector arrs; - arrs.push_back(DtoSlicePtr(toElem(exp2))); - CatExp *ce = static_cast(exp1); - do - { - arrs.push_back(DtoSlicePtr(toElem(ce->e2))); - ce = static_cast(ce->e1); - } while (ce->op == TOKcat); - arrs.push_back(DtoSlicePtr(toElem(ce))); - - // Create static array from slices - LLPointerType* ptrarraytype = isaPointer(arrs[0]->getType()); - assert(ptrarraytype && "Expected pointer type"); - LLStructType* arraytype = isaStruct(ptrarraytype->getElementType()); - assert(arraytype && "Expected struct type"); - LLArrayType* type = LLArrayType::get(arraytype, arrs.size()); - LLValue* array = DtoRawAlloca(type, 0, ".slicearray"); - unsigned int i = 0; - for (ArgVector::reverse_iterator I = arrs.rbegin(), E = arrs.rend(); I != E; ++I) - { - LLValue* v = DtoLoad(DtoBitCast(*I, ptrarraytype)); - DtoStore(v, DtoGEPi(array, 0, i++, ".slice")); - } - - LLStructType* type2 = DtoArrayType(arraytype); - LLValue* array2 = DtoRawAlloca(type2, 0, ".array"); - DtoStore(DtoConstSize_t(arrs.size()), DtoGEPi(array2, 0, 0, ".len")); - DtoStore(DtoBitCast(array, ptrarraytype), DtoGEPi(array2, 0, 1, ".ptr")); - LLValue* val = DtoLoad(DtoBitCast(array2, getPtrToType(DtoArrayType(DtoArrayType(LLType::getInt8Ty(gIR->context())))))); - - // TypeInfo ti - args.push_back(DtoTypeInfoOf(arrayType)); - // byte[][] arrs - args.push_back(val); - } - else - { - fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_arraycatT"); - - // TypeInfo ti - args.push_back(DtoTypeInfoOf(arrayType)); - // byte[] x - LLValue *val = DtoLoad(DtoSlicePtr(toElem(exp1))); - val = DtoAggrPaint(val, fn->getFunctionType()->getParamType(1)); - args.push_back(val); - // byte[] y - val = DtoLoad(DtoSlicePtr(toElem(exp2))); - val = DtoAggrPaint(val, fn->getFunctionType()->getParamType(2)); - args.push_back(val); - } - - LLValue *newArray = gIR->func()->scopes->callOrInvoke(fn, args, ".appendedArray").getInstruction(); - return getSlice(arrayType, newArray); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DSliceValue* DtoAppendDChar(Loc& loc, DValue* arr, Expression* exp, const char *func) -{ - Type *arrayType = arr->getType(); - DValue* valueToAppend = toElem(exp); - - // Prepare arguments - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, func); - - // Call function (ref string x, dchar c) - LLValue* newArray = gIR->CreateCallOrInvoke(fn, - DtoBitCast(arr->getLVal(), fn->getFunctionType()->getParamType(0)), - DtoBitCast(valueToAppend->getRVal(), fn->getFunctionType()->getParamType(1)), - ".appendedArray" - ).getInstruction(); - - return getSlice(arrayType, newArray); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DSliceValue* DtoAppendDCharToString(Loc& loc, DValue* arr, Expression* exp) -{ - IF_LOG Logger::println("DtoAppendDCharToString"); - LOG_SCOPE; - return DtoAppendDChar(loc, arr, exp, "_d_arrayappendcd"); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DSliceValue* DtoAppendDCharToUnicodeString(Loc& loc, DValue* arr, Expression* exp) -{ - IF_LOG Logger::println("DtoAppendDCharToUnicodeString"); - LOG_SCOPE; - return DtoAppendDChar(loc, arr, exp, "_d_arrayappendwd"); +DSliceValue *DtoAppendDCharToUnicodeString(Loc &loc, DValue *arr, + Expression *exp) { + IF_LOG Logger::println("DtoAppendDCharToUnicodeString"); + LOG_SCOPE; + return DtoAppendDChar(loc, arr, exp, "_d_arrayappendwd"); } ////////////////////////////////////////////////////////////////////////////////////////// // helper for eq and cmp -static LLValue* DtoArrayEqCmp_impl(Loc& loc, const char* func, DValue* l, DValue* r, bool useti) -{ - IF_LOG Logger::println("comparing arrays"); - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, func); - assert(fn); +static LLValue *DtoArrayEqCmp_impl(Loc &loc, const char *func, DValue *l, + DValue *r, bool useti) { + IF_LOG Logger::println("comparing arrays"); + LLFunction *fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, func); + assert(fn); - // find common dynamic array type - Type* commonType = l->getType()->toBasetype()->nextOf()->arrayOf(); + // find common dynamic array type + Type *commonType = l->getType()->toBasetype()->nextOf()->arrayOf(); - // cast static arrays to dynamic ones, this turns them into DSliceValues - Logger::println("casting to dynamic arrays"); - l = DtoCastArray(loc, l, commonType); - r = DtoCastArray(loc, r, commonType); + // cast static arrays to dynamic ones, this turns them into DSliceValues + Logger::println("casting to dynamic arrays"); + l = DtoCastArray(loc, l, commonType); + r = DtoCastArray(loc, r, commonType); - LLSmallVector args; + LLSmallVector args; - // get values, reinterpret cast to void[] - args.push_back(DtoAggrPaint(l->getRVal(), DtoArrayType(LLType::getInt8Ty(gIR->context())))); - args.push_back(DtoAggrPaint(r->getRVal(), DtoArrayType(LLType::getInt8Ty(gIR->context())))); + // get values, reinterpret cast to void[] + args.push_back(DtoAggrPaint(l->getRVal(), + DtoArrayType(LLType::getInt8Ty(gIR->context())))); + args.push_back(DtoAggrPaint(r->getRVal(), + DtoArrayType(LLType::getInt8Ty(gIR->context())))); - // pass array typeinfo ? - if (useti) { - Type* t = l->getType(); - LLValue* tival = DtoTypeInfoOf(t); - args.push_back(DtoBitCast(tival, fn->getFunctionType()->getParamType(2))); - } + // pass array typeinfo ? + if (useti) { + Type *t = l->getType(); + LLValue *tival = DtoTypeInfoOf(t); + args.push_back(DtoBitCast(tival, fn->getFunctionType()->getParamType(2))); + } - return gIR->func()->scopes->callOrInvoke(fn, args).getInstruction(); + return gIR->func()->scopes->callOrInvoke(fn, args).getInstruction(); } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoArrayEquals(Loc& loc, TOK op, DValue* l, DValue* r) -{ - LLValue* res = DtoArrayEqCmp_impl(loc, "_adEq2", l, r, true); - res = gIR->ir->CreateICmpNE(res, DtoConstInt(0)); - if (op == TOKnotequal) - res = gIR->ir->CreateNot(res); +LLValue *DtoArrayEquals(Loc &loc, TOK op, DValue *l, DValue *r) { + LLValue *res = DtoArrayEqCmp_impl(loc, "_adEq2", l, r, true); + res = gIR->ir->CreateICmpNE(res, DtoConstInt(0)); + if (op == TOKnotequal) + res = gIR->ir->CreateNot(res); - return res; + return res; } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoArrayCompare(Loc& loc, TOK op, DValue* l, DValue* r) -{ - LLValue* res = 0; - llvm::ICmpInst::Predicate cmpop; - tokToIcmpPred(op, false, &cmpop, &res); +LLValue *DtoArrayCompare(Loc &loc, TOK op, DValue *l, DValue *r) { + LLValue *res = 0; + llvm::ICmpInst::Predicate cmpop; + tokToIcmpPred(op, false, &cmpop, &res); - if (!res) - { - Type* t = l->getType()->toBasetype()->nextOf()->toBasetype(); - if (t->ty == Tchar) - res = DtoArrayEqCmp_impl(loc, "_adCmpChar", l, r, false); - else - res = DtoArrayEqCmp_impl(loc, "_adCmp2", l, r, true); - res = gIR->ir->CreateICmp(cmpop, res, DtoConstInt(0)); - } - - assert(res); - return res; -} - -////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoArrayCastLength(Loc& loc, LLValue* len, LLType* elemty, LLType* newelemty) -{ - IF_LOG Logger::println("DtoArrayCastLength"); - LOG_SCOPE; - - assert(len); - assert(elemty); - assert(newelemty); - - size_t esz = getTypePaddedSize(elemty); - size_t nsz = getTypePaddedSize(newelemty); - if (esz == nsz) - return len; - - LLFunction* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_array_cast_len"); - return gIR->CreateCallOrInvoke( - fn, - len, - LLConstantInt::get(DtoSize_t(), esz, false), - LLConstantInt::get(DtoSize_t(), nsz, false) - ).getInstruction(); -} - -////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoDynArrayIs(TOK op, DValue* l, DValue* r) -{ - LLValue *len1, *ptr1, *len2, *ptr2; - - assert(l); - assert(r); - - // compare lengths - len1 = DtoArrayLen(l); - len2 = DtoArrayLen(r); - LLValue* b1 = gIR->ir->CreateICmpEQ(len1,len2); - - // compare pointers - ptr1 = DtoArrayPtr(l); - ptr2 = DtoArrayPtr(r); - LLValue* b2 = gIR->ir->CreateICmpEQ(ptr1,ptr2); - - // combine - LLValue* res = gIR->ir->CreateAnd(b1,b2); - - // return result - return (op == TOKnotidentity) ? gIR->ir->CreateNot(res) : res; -} - -////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoArrayLen(DValue* v) -{ - IF_LOG Logger::println("DtoArrayLen"); - LOG_SCOPE; - - Type* t = v->getType()->toBasetype(); - if (t->ty == Tarray) { - if (DSliceValue* s = v->isSlice()) - return s->len; - else if (v->isNull()) - return DtoConstSize_t(0); - else if (v->isLVal()) - return DtoLoad(DtoGEPi(v->getLVal(), 0,0), ".len"); - return gIR->ir->CreateExtractValue(v->getRVal(), 0, ".len"); - } - else if (t->ty == Tsarray) { - assert(!v->isSlice()); - assert(!v->isNull()); - assert(v->type->toBasetype()->ty == Tsarray); - TypeSArray *sarray = static_cast(v->type->toBasetype()); - return DtoConstSize_t(sarray->dim->toUInteger()); - } - llvm_unreachable("unsupported array for len"); -} - -////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoArrayPtr(DValue* v) -{ - IF_LOG Logger::println("DtoArrayPtr"); - LOG_SCOPE; - - Type* t = v->getType()->toBasetype(); - // v's LL array element type may not be the real one - // due to implicit casts (e.g., to base class) - LLType* wantedLLPtrType = DtoPtrToType(t->nextOf()); - LLValue* ptr = NULL; - - if (t->ty == Tarray) - { - if (DSliceValue* s = v->isSlice()) - ptr = s->ptr; - else if (v->isNull()) - ptr = getNullPtr(wantedLLPtrType); - else if (v->isLVal()) - ptr = DtoLoad(DtoGEPi(v->getLVal(), 0, 1), ".ptr"); - else - ptr = gIR->ir->CreateExtractValue(v->getRVal(), 1, ".ptr"); - } - else if (t->ty == Tsarray) - { - assert(!v->isSlice()); - assert(!v->isNull()); - ptr = DtoGEPi(v->getRVal(), 0, 0, "sarrayptr"); - } + if (!res) { + Type *t = l->getType()->toBasetype()->nextOf()->toBasetype(); + if (t->ty == Tchar) + res = DtoArrayEqCmp_impl(loc, "_adCmpChar", l, r, false); else - llvm_unreachable("Unexpected array type."); + res = DtoArrayEqCmp_impl(loc, "_adCmp2", l, r, true); + res = gIR->ir->CreateICmp(cmpop, res, DtoConstInt(0)); + } - return DtoBitCast(ptr, wantedLLPtrType); + assert(res); + return res; } ////////////////////////////////////////////////////////////////////////////////////////// -DValue* DtoCastArray(Loc& loc, DValue* u, Type* to) -{ - IF_LOG Logger::println("DtoCastArray"); - LOG_SCOPE; +LLValue *DtoArrayCastLength(Loc &loc, LLValue *len, LLType *elemty, + LLType *newelemty) { + IF_LOG Logger::println("DtoArrayCastLength"); + LOG_SCOPE; - LLType* tolltype = DtoType(to); + assert(len); + assert(elemty); + assert(newelemty); - Type* totype = to->toBasetype(); - Type* fromtype = u->getType()->toBasetype(); - if (fromtype->ty != Tarray && fromtype->ty != Tsarray) { - error(loc, "can't cast %s to %s", u->getType()->toChars(), to->toChars()); + size_t esz = getTypePaddedSize(elemty); + size_t nsz = getTypePaddedSize(newelemty); + if (esz == nsz) + return len; + + LLFunction *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_array_cast_len"); + return gIR->CreateCallOrInvoke(fn, len, + LLConstantInt::get(DtoSize_t(), esz, false), + LLConstantInt::get(DtoSize_t(), nsz, false)) + .getInstruction(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +LLValue *DtoDynArrayIs(TOK op, DValue *l, DValue *r) { + LLValue *len1, *ptr1, *len2, *ptr2; + + assert(l); + assert(r); + + // compare lengths + len1 = DtoArrayLen(l); + len2 = DtoArrayLen(r); + LLValue *b1 = gIR->ir->CreateICmpEQ(len1, len2); + + // compare pointers + ptr1 = DtoArrayPtr(l); + ptr2 = DtoArrayPtr(r); + LLValue *b2 = gIR->ir->CreateICmpEQ(ptr1, ptr2); + + // combine + LLValue *res = gIR->ir->CreateAnd(b1, b2); + + // return result + return (op == TOKnotidentity) ? gIR->ir->CreateNot(res) : res; +} + +////////////////////////////////////////////////////////////////////////////////////////// +LLValue *DtoArrayLen(DValue *v) { + IF_LOG Logger::println("DtoArrayLen"); + LOG_SCOPE; + + Type *t = v->getType()->toBasetype(); + if (t->ty == Tarray) { + if (DSliceValue *s = v->isSlice()) + return s->len; + else if (v->isNull()) + return DtoConstSize_t(0); + else if (v->isLVal()) + return DtoLoad(DtoGEPi(v->getLVal(), 0, 0), ".len"); + return gIR->ir->CreateExtractValue(v->getRVal(), 0, ".len"); + } else if (t->ty == Tsarray) { + assert(!v->isSlice()); + assert(!v->isNull()); + assert(v->type->toBasetype()->ty == Tsarray); + TypeSArray *sarray = static_cast(v->type->toBasetype()); + return DtoConstSize_t(sarray->dim->toUInteger()); + } + llvm_unreachable("unsupported array for len"); +} + +////////////////////////////////////////////////////////////////////////////////////////// +LLValue *DtoArrayPtr(DValue *v) { + IF_LOG Logger::println("DtoArrayPtr"); + LOG_SCOPE; + + Type *t = v->getType()->toBasetype(); + // v's LL array element type may not be the real one + // due to implicit casts (e.g., to base class) + LLType *wantedLLPtrType = DtoPtrToType(t->nextOf()); + LLValue *ptr = NULL; + + if (t->ty == Tarray) { + if (DSliceValue *s = v->isSlice()) + ptr = s->ptr; + else if (v->isNull()) + ptr = getNullPtr(wantedLLPtrType); + else if (v->isLVal()) + ptr = DtoLoad(DtoGEPi(v->getLVal(), 0, 1), ".ptr"); + else + ptr = gIR->ir->CreateExtractValue(v->getRVal(), 1, ".ptr"); + } else if (t->ty == Tsarray) { + assert(!v->isSlice()); + assert(!v->isNull()); + ptr = DtoGEPi(v->getRVal(), 0, 0, "sarrayptr"); + } else + llvm_unreachable("Unexpected array type."); + + return DtoBitCast(ptr, wantedLLPtrType); +} + +////////////////////////////////////////////////////////////////////////////////////////// +DValue *DtoCastArray(Loc &loc, DValue *u, Type *to) { + IF_LOG Logger::println("DtoCastArray"); + LOG_SCOPE; + + LLType *tolltype = DtoType(to); + + Type *totype = to->toBasetype(); + Type *fromtype = u->getType()->toBasetype(); + if (fromtype->ty != Tarray && fromtype->ty != Tsarray) { + error(loc, "can't cast %s to %s", u->getType()->toChars(), to->toChars()); + fatal(); + } + + LLValue *rval; + LLValue *rval2; + bool isslice = false; + + IF_LOG Logger::cout() << "from array or sarray" << '\n'; + + if (totype->ty == Tpointer) { + IF_LOG Logger::cout() << "to pointer" << '\n'; + rval = DtoArrayPtr(u); + if (rval->getType() != tolltype) + rval = gIR->ir->CreateBitCast(rval, tolltype); + } else if (totype->ty == Tarray) { + IF_LOG Logger::cout() << "to array" << '\n'; + + LLType *ptrty = DtoArrayType(totype)->getContainedType(1); + LLType *ety = DtoMemType(fromtype->nextOf()); + + if (fromtype->ty == Tsarray) { + LLValue *uval = u->getRVal(); + + IF_LOG Logger::cout() << "uvalTy = " << *uval->getType() << '\n'; + + assert(isaPointer(uval->getType())); + LLArrayType *arrty = isaArray(uval->getType()->getContainedType(0)); + + if (arrty->getNumElements() * fromtype->nextOf()->size() % + totype->nextOf()->size() != + 0) { + error(loc, + "invalid cast from '%s' to '%s', the element sizes don't line up", + fromtype->toChars(), totype->toChars()); fatal(); + } + + uinteger_t len = static_cast(fromtype)->dim->toUInteger(); + rval2 = LLConstantInt::get(DtoSize_t(), len, false); + if (fromtype->nextOf()->size() != totype->nextOf()->size()) + rval2 = DtoArrayCastLength(loc, rval2, ety, ptrty->getContainedType(0)); + rval = DtoBitCast(uval, ptrty); + } else { + rval2 = DtoArrayLen(u); + if (fromtype->nextOf()->size() != totype->nextOf()->size()) + rval2 = DtoArrayCastLength(loc, rval2, ety, ptrty->getContainedType(0)); + + rval = DtoArrayPtr(u); + rval = DtoBitCast(rval, ptrty); } + isslice = true; + } else if (totype->ty == Tsarray) { + IF_LOG Logger::cout() << "to sarray" << '\n'; - LLValue* rval; - LLValue* rval2; - bool isslice = false; + size_t tosize = static_cast(totype)->dim->toInteger(); - IF_LOG Logger::cout() << "from array or sarray" << '\n'; + if (fromtype->ty == Tsarray) { + LLValue *uval = u->getRVal(); + IF_LOG Logger::cout() << "uvalTy = " << *uval->getType() << '\n'; + rval = DtoBitCast(uval, getPtrToType(tolltype)); + } else { + size_t i = + (tosize * totype->nextOf()->size() - 1) / fromtype->nextOf()->size(); + DConstValue index(Type::tsize_t, DtoConstSize_t(i)); + DtoIndexBoundsCheck(loc, u, &index); - if (totype->ty == Tpointer) { - IF_LOG Logger::cout() << "to pointer" << '\n'; - rval = DtoArrayPtr(u); - if (rval->getType() != tolltype) - rval = gIR->ir->CreateBitCast(rval, tolltype); + rval = DtoArrayPtr(u); + rval = DtoBitCast(rval, getPtrToType(tolltype)); } - else if (totype->ty == Tarray) { - IF_LOG Logger::cout() << "to array" << '\n'; + } else if (totype->ty == Tbool) { + // return (arr.ptr !is null) + LLValue *ptr = DtoArrayPtr(u); + LLConstant *nul = getNullPtr(ptr->getType()); + rval = gIR->ir->CreateICmpNE(ptr, nul); + } else { + rval = DtoArrayPtr(u); + rval = DtoBitCast(rval, getPtrToType(tolltype)); + if (totype->ty != Tstruct) + rval = DtoLoad(rval); + } - LLType* ptrty = DtoArrayType(totype)->getContainedType(1); - LLType* ety = DtoMemType(fromtype->nextOf()); + if (isslice) { + Logger::println("isslice"); + return new DSliceValue(to, rval2, rval); + } - if (fromtype->ty == Tsarray) { - LLValue* uval = u->getRVal(); - - IF_LOG Logger::cout() << "uvalTy = " << *uval->getType() << '\n'; - - assert(isaPointer(uval->getType())); - LLArrayType* arrty = isaArray(uval->getType()->getContainedType(0)); - - if(arrty->getNumElements()*fromtype->nextOf()->size() % totype->nextOf()->size() != 0) - { - error(loc, "invalid cast from '%s' to '%s', the element sizes don't line up", fromtype->toChars(), totype->toChars()); - fatal(); - } - - uinteger_t len = static_cast(fromtype)->dim->toUInteger(); - rval2 = LLConstantInt::get(DtoSize_t(), len, false); - if (fromtype->nextOf()->size() != totype->nextOf()->size()) - rval2 = DtoArrayCastLength(loc, rval2, ety, ptrty->getContainedType(0)); - rval = DtoBitCast(uval, ptrty); - } - else { - rval2 = DtoArrayLen(u); - if (fromtype->nextOf()->size() != totype->nextOf()->size()) - rval2 = DtoArrayCastLength(loc, rval2, ety, ptrty->getContainedType(0)); - - rval = DtoArrayPtr(u); - rval = DtoBitCast(rval, ptrty); - } - isslice = true; - } - else if (totype->ty == Tsarray) { - IF_LOG Logger::cout() << "to sarray" << '\n'; - - size_t tosize = static_cast(totype)->dim->toInteger(); - - if (fromtype->ty == Tsarray) { - LLValue* uval = u->getRVal(); - IF_LOG Logger::cout() << "uvalTy = " << *uval->getType() << '\n'; - rval = DtoBitCast(uval, getPtrToType(tolltype)); - } - else { - size_t i = (tosize * totype->nextOf()->size() - 1) / fromtype->nextOf()->size(); - DConstValue index(Type::tsize_t, DtoConstSize_t(i)); - DtoIndexBoundsCheck(loc, u, &index); - - rval = DtoArrayPtr(u); - rval = DtoBitCast(rval, getPtrToType(tolltype)); - } - } - else if (totype->ty == Tbool) { - // return (arr.ptr !is null) - LLValue* ptr = DtoArrayPtr(u); - LLConstant* nul = getNullPtr(ptr->getType()); - rval = gIR->ir->CreateICmpNE(ptr, nul); - } - else { - rval = DtoArrayPtr(u); - rval = DtoBitCast(rval, getPtrToType(tolltype)); - if (totype->ty != Tstruct) - rval = DtoLoad(rval); - } - - if (isslice) { - Logger::println("isslice"); - return new DSliceValue(to, rval2, rval); - } - - return new DImValue(to, rval); + return new DImValue(to, rval); } -void DtoIndexBoundsCheck(Loc& loc, DValue* arr, DValue* index) -{ - Type* arrty = arr->getType()->toBasetype(); - assert((arrty->ty == Tsarray || arrty->ty == Tarray || arrty->ty == Tpointer) && - "Can only array bounds check for static or dynamic arrays"); +void DtoIndexBoundsCheck(Loc &loc, DValue *arr, DValue *index) { + Type *arrty = arr->getType()->toBasetype(); + assert( + (arrty->ty == Tsarray || arrty->ty == Tarray || arrty->ty == Tpointer) && + "Can only array bounds check for static or dynamic arrays"); - if (!index) { - // Caller supplied no index, known in-bounds. - return; - } + if (!index) { + // Caller supplied no index, known in-bounds. + return; + } - if (arrty->ty == Tpointer) { - // Length of pointers is unknown, ingore. - return; - } + if (arrty->ty == Tpointer) { + // Length of pointers is unknown, ingore. + return; + } - llvm::ICmpInst::Predicate cmpop = llvm::ICmpInst::ICMP_ULT; - llvm::Value* cond = gIR->ir->CreateICmp(cmpop, index->getRVal(), - DtoArrayLen(arr), "bounds.cmp"); + llvm::ICmpInst::Predicate cmpop = llvm::ICmpInst::ICMP_ULT; + llvm::Value *cond = gIR->ir->CreateICmp(cmpop, index->getRVal(), + DtoArrayLen(arr), "bounds.cmp"); - llvm::BasicBlock* failbb = llvm::BasicBlock::Create(gIR->context(), - "bounds.fail", gIR->topfunc()); - llvm::BasicBlock* okbb = llvm::BasicBlock::Create(gIR->context(), - "bounds.ok", gIR->topfunc()); - gIR->ir->CreateCondBr(cond, okbb, failbb); + llvm::BasicBlock *failbb = + llvm::BasicBlock::Create(gIR->context(), "bounds.fail", gIR->topfunc()); + llvm::BasicBlock *okbb = + llvm::BasicBlock::Create(gIR->context(), "bounds.ok", gIR->topfunc()); + gIR->ir->CreateCondBr(cond, okbb, failbb); - // set up failbb to call the array bounds error runtime function - gIR->scope() = IRScope(failbb); + // set up failbb to call the array bounds error runtime function + gIR->scope() = IRScope(failbb); - DtoBoundsCheckFailCall(gIR, loc); + DtoBoundsCheckFailCall(gIR, loc); - // if ok, proceed in okbb - gIR->scope() = IRScope(okbb); + // if ok, proceed in okbb + gIR->scope() = IRScope(okbb); } -void DtoBoundsCheckFailCall(IRState *irs, Loc &loc) -{ - llvm::Function* errorfn = LLVM_D_GetRuntimeFunction(loc, irs->module, "_d_arraybounds"); - irs->CreateCallOrInvoke( - errorfn, - DtoModuleFileName(irs->func()->decl->getModule(), loc), - DtoConstUint(loc.linnum) - ); +void DtoBoundsCheckFailCall(IRState *irs, Loc &loc) { + llvm::Function *errorfn = + LLVM_D_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(); + // the function does not return + irs->ir->CreateUnreachable(); } diff --git a/gen/arrays.h b/gen/arrays.h index b5a5aa9547..60b7daee68 100644 --- a/gen/arrays.h +++ b/gen/arrays.h @@ -26,58 +26,64 @@ struct IRState; struct Loc; class Type; -llvm::StructType* DtoArrayType(Type* arrayTy); -llvm::StructType* DtoArrayType(LLType* elemTy); -llvm::ArrayType* DtoStaticArrayType(Type* sarrayTy); +llvm::StructType *DtoArrayType(Type *arrayTy); +llvm::StructType *DtoArrayType(LLType *elemTy); +llvm::ArrayType *DtoStaticArrayType(Type *sarrayTy); /// Creates a (global) constant with the element data for the given arary /// initializer. targetType is explicit because the frontend sometimes emits /// ArrayInitializers for vectors typed as static arrays. -LLConstant* DtoConstArrayInitializer(ArrayInitializer* si, Type* targetType); +LLConstant *DtoConstArrayInitializer(ArrayInitializer *si, Type *targetType); -LLConstant* DtoConstSlice(LLConstant* dim, LLConstant* ptr, Type *type = 0); +LLConstant *DtoConstSlice(LLConstant *dim, LLConstant *ptr, Type *type = 0); /// Returns whether the array literal can be evaluated to a (LLVM) constant. -bool isConstLiteral(ArrayLiteralExp* ale); +bool isConstLiteral(ArrayLiteralExp *ale); /// Returns the constant for the given array literal expression. -llvm::Constant* arrayLiteralToConst(IRState* p, ArrayLiteralExp* ale); +llvm::Constant *arrayLiteralToConst(IRState *p, ArrayLiteralExp *ale); /// Initializes a chunk of memory with the contents of an array literal. /// /// dstMem is expected to be a pointer to the array allocation. -void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem); +void initializeArrayLiteral(IRState *p, ArrayLiteralExp *ale, LLValue *dstMem); -void DtoArrayAssign(Loc& loc, DValue* lhs, DValue* rhs, int op, bool canSkipPostblit); -void DtoSetArrayToNull(LLValue* v); +void DtoArrayAssign(Loc &loc, DValue *lhs, DValue *rhs, int op, + bool canSkipPostblit); +void DtoSetArrayToNull(LLValue *v); -DSliceValue* DtoNewDynArray(Loc& loc, Type* arrayType, DValue* dim, bool defaultInit=true); -DSliceValue* DtoNewMulDimDynArray(Loc& loc, Type* arrayType, DValue** dims, size_t ndims); -DSliceValue* DtoResizeDynArray(Loc& loc, Type* arrayType, DValue* array, llvm::Value* newdim); +DSliceValue *DtoNewDynArray(Loc &loc, Type *arrayType, DValue *dim, + bool defaultInit = true); +DSliceValue *DtoNewMulDimDynArray(Loc &loc, Type *arrayType, DValue **dims, + size_t ndims); +DSliceValue *DtoResizeDynArray(Loc &loc, Type *arrayType, DValue *array, + llvm::Value *newdim); -void DtoCatAssignElement(Loc& loc, Type* type, DValue* arr, Expression* exp); -DSliceValue* DtoCatAssignArray(Loc& loc, DValue* arr, Expression* exp); -DSliceValue* DtoCatArrays(Loc& loc, Type* type, Expression* e1, Expression* e2); -DSliceValue* DtoAppendDCharToString(Loc& loc, DValue* arr, Expression* exp); -DSliceValue* DtoAppendDCharToUnicodeString(Loc& loc, DValue* arr, Expression* exp); +void DtoCatAssignElement(Loc &loc, Type *type, DValue *arr, Expression *exp); +DSliceValue *DtoCatAssignArray(Loc &loc, DValue *arr, Expression *exp); +DSliceValue *DtoCatArrays(Loc &loc, Type *type, Expression *e1, Expression *e2); +DSliceValue *DtoAppendDCharToString(Loc &loc, DValue *arr, Expression *exp); +DSliceValue *DtoAppendDCharToUnicodeString(Loc &loc, DValue *arr, + Expression *exp); -LLValue* DtoArrayEquals(Loc& loc, TOK op, DValue* l, DValue* r); -LLValue* DtoArrayCompare(Loc& loc, TOK op, DValue* l, DValue* r); +LLValue *DtoArrayEquals(Loc &loc, TOK op, DValue *l, DValue *r); +LLValue *DtoArrayCompare(Loc &loc, TOK op, DValue *l, DValue *r); -LLValue* DtoDynArrayIs(TOK op, DValue* l, DValue* r); +LLValue *DtoDynArrayIs(TOK op, DValue *l, DValue *r); -LLValue* DtoArrayCastLength(Loc& loc, LLValue* len, LLType* elemty, LLType* newelemty); +LLValue *DtoArrayCastLength(Loc &loc, LLValue *len, LLType *elemty, + LLType *newelemty); -LLValue* DtoArrayLen(DValue* v); -LLValue* DtoArrayPtr(DValue* v); +LLValue *DtoArrayLen(DValue *v); +LLValue *DtoArrayPtr(DValue *v); -DValue* DtoCastArray(Loc& loc, DValue* val, Type* to); +DValue *DtoCastArray(Loc &loc, DValue *val, Type *to); // generates an array bounds check -void DtoIndexBoundsCheck(Loc& loc, DValue* arr, DValue* index); +void DtoIndexBoundsCheck(Loc &loc, DValue *arr, DValue *index); /// Inserts a call to the druntime function that throws the range error, with /// the given location. -void DtoBoundsCheckFailCall(IRState* p, Loc& loc); +void DtoBoundsCheckFailCall(IRState *p, Loc &loc); #endif // LDC_GEN_ARRAYS_H diff --git a/gen/asm-x86.h b/gen/asm-x86.h index 009c30ee2e..edf6f04aa2 100644 --- a/gen/asm-x86.h +++ b/gen/asm-x86.h @@ -27,649 +27,805 @@ namespace AsmParserx8632 #else namespace AsmParserx8664 #endif -{ - - typedef enum { - Reg_Invalid = -1, - Reg_EAX = 0, - Reg_EBX, - Reg_ECX, - Reg_EDX, - Reg_ESI, - Reg_EDI, - Reg_EBP, - Reg_ESP, - Reg_ST, - Reg_ST1, Reg_ST2, Reg_ST3, Reg_ST4, Reg_ST5, Reg_ST6, Reg_ST7, - Reg_MM0, Reg_MM1, Reg_MM2, Reg_MM3, Reg_MM4, Reg_MM5, Reg_MM6, Reg_MM7, - Reg_XMM0, Reg_XMM1, Reg_XMM2, Reg_XMM3, Reg_XMM4, Reg_XMM5, Reg_XMM6, Reg_XMM7, + +typedef enum { + Reg_Invalid = -1, + Reg_EAX = 0, + Reg_EBX, + Reg_ECX, + Reg_EDX, + Reg_ESI, + Reg_EDI, + Reg_EBP, + Reg_ESP, + Reg_ST, + Reg_ST1, + Reg_ST2, + Reg_ST3, + Reg_ST4, + Reg_ST5, + Reg_ST6, + Reg_ST7, + Reg_MM0, + Reg_MM1, + Reg_MM2, + Reg_MM3, + Reg_MM4, + Reg_MM5, + Reg_MM6, + Reg_MM7, + Reg_XMM0, + Reg_XMM1, + Reg_XMM2, + Reg_XMM3, + Reg_XMM4, + Reg_XMM5, + Reg_XMM6, + Reg_XMM7, #ifdef ASM_X86_64 - Reg_RAX, Reg_RBX, Reg_RCX, Reg_RDX, Reg_RSI, Reg_RDI, Reg_RBP, Reg_RSP, - Reg_R8, Reg_R9, Reg_R10, Reg_R11, Reg_R12, Reg_R13, Reg_R14, Reg_R15, - Reg_R8B, Reg_R9B, Reg_R10B, Reg_R11B, Reg_R12B, Reg_R13B, Reg_R14B, Reg_R15B, - Reg_R8W, Reg_R9W, Reg_R10W, Reg_R11W, Reg_R12W, Reg_R13W, Reg_R14W, Reg_R15W, - Reg_R8D, Reg_R9D, Reg_R10D, Reg_R11D, Reg_R12D, Reg_R13D, Reg_R14D, Reg_R15D, - Reg_XMM8, Reg_XMM9, Reg_XMM10, Reg_XMM11, Reg_XMM12, Reg_XMM13, Reg_XMM14, Reg_XMM15, - Reg_RIP, - Reg_SIL, Reg_DIL, Reg_BPL, Reg_SPL, + Reg_RAX, + Reg_RBX, + Reg_RCX, + Reg_RDX, + Reg_RSI, + Reg_RDI, + Reg_RBP, + Reg_RSP, + Reg_R8, + Reg_R9, + Reg_R10, + Reg_R11, + Reg_R12, + Reg_R13, + Reg_R14, + Reg_R15, + Reg_R8B, + Reg_R9B, + Reg_R10B, + Reg_R11B, + Reg_R12B, + Reg_R13B, + Reg_R14B, + Reg_R15B, + Reg_R8W, + Reg_R9W, + Reg_R10W, + Reg_R11W, + Reg_R12W, + Reg_R13W, + Reg_R14W, + Reg_R15W, + Reg_R8D, + Reg_R9D, + Reg_R10D, + Reg_R11D, + Reg_R12D, + Reg_R13D, + Reg_R14D, + Reg_R15D, + Reg_XMM8, + Reg_XMM9, + Reg_XMM10, + Reg_XMM11, + Reg_XMM12, + Reg_XMM13, + Reg_XMM14, + Reg_XMM15, + Reg_RIP, + Reg_SIL, + Reg_DIL, + Reg_BPL, + Reg_SPL, #endif - Reg_EFLAGS, - Reg_CS, - Reg_DS, - Reg_SS, - Reg_ES, - Reg_FS, - Reg_GS, - Reg_AX, Reg_BX, Reg_CX, Reg_DX, Reg_SI, Reg_DI, Reg_BP, Reg_SP, - Reg_AL, Reg_AH, Reg_BL, Reg_BH, Reg_CL, Reg_CH, Reg_DL, Reg_DH, - Reg_CR0, Reg_CR2, Reg_CR3, Reg_CR4, - Reg_DR0, Reg_DR1, Reg_DR2, Reg_DR3, Reg_DR6, Reg_DR7, - Reg_TR3, Reg_TR4, Reg_TR5, Reg_TR6, Reg_TR7 - } Reg; + Reg_EFLAGS, + Reg_CS, + Reg_DS, + Reg_SS, + Reg_ES, + Reg_FS, + Reg_GS, + Reg_AX, + Reg_BX, + Reg_CX, + Reg_DX, + Reg_SI, + Reg_DI, + Reg_BP, + Reg_SP, + Reg_AL, + Reg_AH, + Reg_BL, + Reg_BH, + Reg_CL, + Reg_CH, + Reg_DL, + Reg_DH, + Reg_CR0, + Reg_CR2, + Reg_CR3, + Reg_CR4, + Reg_DR0, + Reg_DR1, + Reg_DR2, + Reg_DR3, + Reg_DR6, + Reg_DR7, + Reg_TR3, + Reg_TR4, + Reg_TR5, + Reg_TR6, + Reg_TR7 +} Reg; - static const int N_Regs = /*gp*/ 8 + /*fp*/ 8 + /*mmx*/ 8 + /*sse*/ 8 + - /*seg*/ 6 + /*16bit*/ 8 + /*8bit*/ 8 + /*sys*/ 4+6+5 + /*flags*/ + 1 +static const int N_Regs = /*gp*/ 8 + /*fp*/ 8 + /*mmx*/ 8 + /*sse*/ 8 + + /*seg*/ 6 + /*16bit*/ 8 + /*8bit*/ 8 + /*sys*/ 4 + 6 + + 5 + /*flags*/ +1 #ifdef ASM_X86_64 - + 8 /*RAX, etc*/ - + 8 /*R8-15*/ - + 4 /*SIL, etc. 8-bit*/ - + 8 /*R8-15B*/ - + 8 /*R8-15W*/ - + 8 /*R8-15D*/ - + 8 /*XMM8-15*/ - + 1 /*RIP*/ + + 8 /*RAX, etc*/ + + 8 /*R8-15*/ + + 4 /*SIL, etc. 8-bit*/ + + 8 /*R8-15B*/ + + 8 /*R8-15W*/ + + 8 /*R8-15D*/ + + 8 /*XMM8-15*/ + + 1 /*RIP*/ #endif - ; + ; #define NULL_TREE "" - static struct - { - const char * name; - std::string gccName; // GAS will take upper case, but GCC won't (needed for the clobber list) - Identifier * ident; - char size; - signed char baseReg; // %% todo: Reg, Reg_XX - } regInfo[N_Regs] = - { - { "EAX", NULL_TREE, NULL, 4, Reg_EAX }, - { "EBX", NULL_TREE, NULL, 4, Reg_EBX }, - { "ECX", NULL_TREE, NULL, 4, Reg_ECX }, - { "EDX", NULL_TREE, NULL, 4, Reg_EDX }, - { "ESI", NULL_TREE, NULL, 4, Reg_ESI }, - { "EDI", NULL_TREE, NULL, 4, Reg_EDI }, - { "EBP", NULL_TREE, NULL, 4, Reg_EBP }, - { "ESP", NULL_TREE, NULL, 4, Reg_ESP }, - { "ST", NULL_TREE, NULL, 10, Reg_ST }, - { "ST(1)", NULL_TREE, NULL, 10, Reg_ST1 }, - { "ST(2)", NULL_TREE, NULL, 10, Reg_ST2 }, - { "ST(3)", NULL_TREE, NULL, 10, Reg_ST3 }, - { "ST(4)", NULL_TREE, NULL, 10, Reg_ST4 }, - { "ST(5)", NULL_TREE, NULL, 10, Reg_ST5 }, - { "ST(6)", NULL_TREE, NULL, 10, Reg_ST6 }, - { "ST(7)", NULL_TREE, NULL, 10, Reg_ST7 }, - { "MM0", NULL_TREE, NULL, 8, Reg_MM0 }, - { "MM1", NULL_TREE, NULL, 8, Reg_MM1 }, - { "MM2", NULL_TREE, NULL, 8, Reg_MM2 }, - { "MM3", NULL_TREE, NULL, 8, Reg_MM3 }, - { "MM4", NULL_TREE, NULL, 8, Reg_MM4 }, - { "MM5", NULL_TREE, NULL, 8, Reg_MM5 }, - { "MM6", NULL_TREE, NULL, 8, Reg_MM6 }, - { "MM7", NULL_TREE, NULL, 8, Reg_MM7 }, - { "XMM0", NULL_TREE, NULL, 16, Reg_XMM0 }, - { "XMM1", NULL_TREE, NULL, 16, Reg_XMM1 }, - { "XMM2", NULL_TREE, NULL, 16, Reg_XMM2 }, - { "XMM3", NULL_TREE, NULL, 16, Reg_XMM3 }, - { "XMM4", NULL_TREE, NULL, 16, Reg_XMM4 }, - { "XMM5", NULL_TREE, NULL, 16, Reg_XMM5 }, - { "XMM6", NULL_TREE, NULL, 16, Reg_XMM6 }, - { "XMM7", NULL_TREE, NULL, 16, Reg_XMM7 }, +static struct { + const char *name; + std::string gccName; // GAS will take upper case, but GCC won't (needed for + // the clobber list) + Identifier *ident; + char size; + signed char baseReg; // %% todo: Reg, Reg_XX +} regInfo[N_Regs] = { + {"EAX", NULL_TREE, NULL, 4, Reg_EAX}, + {"EBX", NULL_TREE, NULL, 4, Reg_EBX}, + {"ECX", NULL_TREE, NULL, 4, Reg_ECX}, + {"EDX", NULL_TREE, NULL, 4, Reg_EDX}, + {"ESI", NULL_TREE, NULL, 4, Reg_ESI}, + {"EDI", NULL_TREE, NULL, 4, Reg_EDI}, + {"EBP", NULL_TREE, NULL, 4, Reg_EBP}, + {"ESP", NULL_TREE, NULL, 4, Reg_ESP}, + {"ST", NULL_TREE, NULL, 10, Reg_ST}, + {"ST(1)", NULL_TREE, NULL, 10, Reg_ST1}, + {"ST(2)", NULL_TREE, NULL, 10, Reg_ST2}, + {"ST(3)", NULL_TREE, NULL, 10, Reg_ST3}, + {"ST(4)", NULL_TREE, NULL, 10, Reg_ST4}, + {"ST(5)", NULL_TREE, NULL, 10, Reg_ST5}, + {"ST(6)", NULL_TREE, NULL, 10, Reg_ST6}, + {"ST(7)", NULL_TREE, NULL, 10, Reg_ST7}, + {"MM0", NULL_TREE, NULL, 8, Reg_MM0}, + {"MM1", NULL_TREE, NULL, 8, Reg_MM1}, + {"MM2", NULL_TREE, NULL, 8, Reg_MM2}, + {"MM3", NULL_TREE, NULL, 8, Reg_MM3}, + {"MM4", NULL_TREE, NULL, 8, Reg_MM4}, + {"MM5", NULL_TREE, NULL, 8, Reg_MM5}, + {"MM6", NULL_TREE, NULL, 8, Reg_MM6}, + {"MM7", NULL_TREE, NULL, 8, Reg_MM7}, + {"XMM0", NULL_TREE, NULL, 16, Reg_XMM0}, + {"XMM1", NULL_TREE, NULL, 16, Reg_XMM1}, + {"XMM2", NULL_TREE, NULL, 16, Reg_XMM2}, + {"XMM3", NULL_TREE, NULL, 16, Reg_XMM3}, + {"XMM4", NULL_TREE, NULL, 16, Reg_XMM4}, + {"XMM5", NULL_TREE, NULL, 16, Reg_XMM5}, + {"XMM6", NULL_TREE, NULL, 16, Reg_XMM6}, + {"XMM7", NULL_TREE, NULL, 16, Reg_XMM7}, #ifdef ASM_X86_64 - { "RAX", NULL_TREE, NULL, 8, Reg_RAX }, - { "RBX", NULL_TREE, NULL, 8, Reg_RBX }, - { "RCX", NULL_TREE, NULL, 8, Reg_RCX }, - { "RDX", NULL_TREE, NULL, 8, Reg_RDX }, - { "RSI", NULL_TREE, NULL, 8, Reg_RSI }, - { "RDI", NULL_TREE, NULL, 8, Reg_RDI }, - { "RBP", NULL_TREE, NULL, 8, Reg_RBP }, - { "RSP", NULL_TREE, NULL, 8, Reg_RSP }, - { "R8", NULL_TREE, NULL, 8, Reg_R8 }, - { "R9", NULL_TREE, NULL, 8, Reg_R9 }, - { "R10", NULL_TREE, NULL, 8, Reg_R10 }, - { "R11", NULL_TREE, NULL, 8, Reg_R11 }, - { "R12", NULL_TREE, NULL, 8, Reg_R12 }, - { "R13", NULL_TREE, NULL, 8, Reg_R13 }, - { "R14", NULL_TREE, NULL, 8, Reg_R14 }, - { "R15", NULL_TREE, NULL, 8, Reg_R15 }, - { "R8B", NULL_TREE, NULL, 1, Reg_R8 }, - { "R9B", NULL_TREE, NULL, 1, Reg_R9 }, - { "R10B", NULL_TREE, NULL, 1, Reg_R10 }, - { "R11B", NULL_TREE, NULL, 1, Reg_R11 }, - { "R12B", NULL_TREE, NULL, 1, Reg_R12 }, - { "R13B", NULL_TREE, NULL, 1, Reg_R13 }, - { "R14B", NULL_TREE, NULL, 1, Reg_R14 }, - { "R15B", NULL_TREE, NULL, 1, Reg_R15 }, - { "R8W", NULL_TREE, NULL, 2, Reg_R8 }, - { "R9W", NULL_TREE, NULL, 2, Reg_R9 }, - { "R10W", NULL_TREE, NULL, 2, Reg_R10 }, - { "R11W", NULL_TREE, NULL, 2, Reg_R11 }, - { "R12W", NULL_TREE, NULL, 2, Reg_R12 }, - { "R13W", NULL_TREE, NULL, 2, Reg_R13 }, - { "R14W", NULL_TREE, NULL, 2, Reg_R14 }, - { "R15W", NULL_TREE, NULL, 2, Reg_R15 }, - { "R8D", NULL_TREE, NULL, 4, Reg_R8 }, - { "R9D", NULL_TREE, NULL, 4, Reg_R9 }, - { "R10D", NULL_TREE, NULL, 4, Reg_R10 }, - { "R11D", NULL_TREE, NULL, 4, Reg_R11 }, - { "R12D", NULL_TREE, NULL, 4, Reg_R12 }, - { "R13D", NULL_TREE, NULL, 4, Reg_R13 }, - { "R14D", NULL_TREE, NULL, 4, Reg_R14 }, - { "R15D", NULL_TREE, NULL, 4, Reg_R15 }, - { "XMM8", NULL_TREE, NULL, 16, Reg_XMM8 }, - { "XMM9", NULL_TREE, NULL, 16, Reg_XMM9 }, - { "XMM10", NULL_TREE, NULL, 16, Reg_XMM10 }, - { "XMM11", NULL_TREE, NULL, 16, Reg_XMM11 }, - { "XMM12", NULL_TREE, NULL, 16, Reg_XMM12 }, - { "XMM13", NULL_TREE, NULL, 16, Reg_XMM13 }, - { "XMM14", NULL_TREE, NULL, 16, Reg_XMM14 }, - { "XMM15", NULL_TREE, NULL, 16, Reg_XMM15 }, - { "RIP", NULL_TREE, NULL, 8, Reg_RIP }, - { "SIL", NULL_TREE, NULL, 1, Reg_SIL }, - { "DIL", NULL_TREE, NULL, 1, Reg_DIL }, - { "BPL", NULL_TREE, NULL, 1, Reg_BPL }, - { "SPL", NULL_TREE, NULL, 1, Reg_SPL }, + {"RAX", NULL_TREE, NULL, 8, Reg_RAX}, + {"RBX", NULL_TREE, NULL, 8, Reg_RBX}, + {"RCX", NULL_TREE, NULL, 8, Reg_RCX}, + {"RDX", NULL_TREE, NULL, 8, Reg_RDX}, + {"RSI", NULL_TREE, NULL, 8, Reg_RSI}, + {"RDI", NULL_TREE, NULL, 8, Reg_RDI}, + {"RBP", NULL_TREE, NULL, 8, Reg_RBP}, + {"RSP", NULL_TREE, NULL, 8, Reg_RSP}, + {"R8", NULL_TREE, NULL, 8, Reg_R8}, + {"R9", NULL_TREE, NULL, 8, Reg_R9}, + {"R10", NULL_TREE, NULL, 8, Reg_R10}, + {"R11", NULL_TREE, NULL, 8, Reg_R11}, + {"R12", NULL_TREE, NULL, 8, Reg_R12}, + {"R13", NULL_TREE, NULL, 8, Reg_R13}, + {"R14", NULL_TREE, NULL, 8, Reg_R14}, + {"R15", NULL_TREE, NULL, 8, Reg_R15}, + {"R8B", NULL_TREE, NULL, 1, Reg_R8}, + {"R9B", NULL_TREE, NULL, 1, Reg_R9}, + {"R10B", NULL_TREE, NULL, 1, Reg_R10}, + {"R11B", NULL_TREE, NULL, 1, Reg_R11}, + {"R12B", NULL_TREE, NULL, 1, Reg_R12}, + {"R13B", NULL_TREE, NULL, 1, Reg_R13}, + {"R14B", NULL_TREE, NULL, 1, Reg_R14}, + {"R15B", NULL_TREE, NULL, 1, Reg_R15}, + {"R8W", NULL_TREE, NULL, 2, Reg_R8}, + {"R9W", NULL_TREE, NULL, 2, Reg_R9}, + {"R10W", NULL_TREE, NULL, 2, Reg_R10}, + {"R11W", NULL_TREE, NULL, 2, Reg_R11}, + {"R12W", NULL_TREE, NULL, 2, Reg_R12}, + {"R13W", NULL_TREE, NULL, 2, Reg_R13}, + {"R14W", NULL_TREE, NULL, 2, Reg_R14}, + {"R15W", NULL_TREE, NULL, 2, Reg_R15}, + {"R8D", NULL_TREE, NULL, 4, Reg_R8}, + {"R9D", NULL_TREE, NULL, 4, Reg_R9}, + {"R10D", NULL_TREE, NULL, 4, Reg_R10}, + {"R11D", NULL_TREE, NULL, 4, Reg_R11}, + {"R12D", NULL_TREE, NULL, 4, Reg_R12}, + {"R13D", NULL_TREE, NULL, 4, Reg_R13}, + {"R14D", NULL_TREE, NULL, 4, Reg_R14}, + {"R15D", NULL_TREE, NULL, 4, Reg_R15}, + {"XMM8", NULL_TREE, NULL, 16, Reg_XMM8}, + {"XMM9", NULL_TREE, NULL, 16, Reg_XMM9}, + {"XMM10", NULL_TREE, NULL, 16, Reg_XMM10}, + {"XMM11", NULL_TREE, NULL, 16, Reg_XMM11}, + {"XMM12", NULL_TREE, NULL, 16, Reg_XMM12}, + {"XMM13", NULL_TREE, NULL, 16, Reg_XMM13}, + {"XMM14", NULL_TREE, NULL, 16, Reg_XMM14}, + {"XMM15", NULL_TREE, NULL, 16, Reg_XMM15}, + {"RIP", NULL_TREE, NULL, 8, Reg_RIP}, + {"SIL", NULL_TREE, NULL, 1, Reg_SIL}, + {"DIL", NULL_TREE, NULL, 1, Reg_DIL}, + {"BPL", NULL_TREE, NULL, 1, Reg_BPL}, + {"SPL", NULL_TREE, NULL, 1, Reg_SPL}, #endif - { "FLAGS", NULL_TREE, NULL, 0, Reg_EFLAGS }, // the gcc name is "flags"; not used in assembler input - { "CS", NULL_TREE, NULL, 2, -1 }, - { "DS", NULL_TREE, NULL, 2, -1 }, - { "SS", NULL_TREE, NULL, 2, -1 }, - { "ES", NULL_TREE, NULL, 2, -1 }, - { "FS", NULL_TREE, NULL, 2, -1 }, - { "GS", NULL_TREE, NULL, 2, -1 }, - { "AX", NULL_TREE, NULL, 2, Reg_EAX }, - { "BX", NULL_TREE, NULL, 2, Reg_EBX }, - { "CX", NULL_TREE, NULL, 2, Reg_ECX }, - { "DX", NULL_TREE, NULL, 2, Reg_EDX }, - { "SI", NULL_TREE, NULL, 2, Reg_ESI }, - { "DI", NULL_TREE, NULL, 2, Reg_EDI }, - { "BP", NULL_TREE, NULL, 2, Reg_EBP }, - { "SP", NULL_TREE, NULL, 2, Reg_ESP }, - { "AL", NULL_TREE, NULL, 1, Reg_EAX }, - { "AH", NULL_TREE, NULL, 1, Reg_EAX }, - { "BL", NULL_TREE, NULL, 1, Reg_EBX }, - { "BH", NULL_TREE, NULL, 1, Reg_EBX }, - { "CL", NULL_TREE, NULL, 1, Reg_ECX }, - { "CH", NULL_TREE, NULL, 1, Reg_ECX }, - { "DL", NULL_TREE, NULL, 1, Reg_EDX }, - { "DH", NULL_TREE, NULL, 1, Reg_EDX }, - { "CR0", NULL_TREE, NULL, 0, -1 }, - { "CR2", NULL_TREE, NULL, 0, -1 }, - { "CR3", NULL_TREE, NULL, 0, -1 }, - { "CR4", NULL_TREE, NULL, 0, -1 }, - { "DR0", NULL_TREE, NULL, 0, -1 }, - { "DR1", NULL_TREE, NULL, 0, -1 }, - { "DR2", NULL_TREE, NULL, 0, -1 }, - { "DR3", NULL_TREE, NULL, 0, -1 }, - { "DR6", NULL_TREE, NULL, 0, -1 }, - { "DR7", NULL_TREE, NULL, 0, -1 }, - { "TR3", NULL_TREE, NULL, 0, -1 }, - { "TR4", NULL_TREE, NULL, 0, -1 }, - { "TR5", NULL_TREE, NULL, 0, -1 }, - { "TR6", NULL_TREE, NULL, 0, -1 }, - { "TR7", NULL_TREE, NULL, 0, -1 } - }; + {"FLAGS", NULL_TREE, NULL, 0, + Reg_EFLAGS}, // the gcc name is "flags"; not used in assembler input + {"CS", NULL_TREE, NULL, 2, -1}, + {"DS", NULL_TREE, NULL, 2, -1}, + {"SS", NULL_TREE, NULL, 2, -1}, + {"ES", NULL_TREE, NULL, 2, -1}, + {"FS", NULL_TREE, NULL, 2, -1}, + {"GS", NULL_TREE, NULL, 2, -1}, + {"AX", NULL_TREE, NULL, 2, Reg_EAX}, + {"BX", NULL_TREE, NULL, 2, Reg_EBX}, + {"CX", NULL_TREE, NULL, 2, Reg_ECX}, + {"DX", NULL_TREE, NULL, 2, Reg_EDX}, + {"SI", NULL_TREE, NULL, 2, Reg_ESI}, + {"DI", NULL_TREE, NULL, 2, Reg_EDI}, + {"BP", NULL_TREE, NULL, 2, Reg_EBP}, + {"SP", NULL_TREE, NULL, 2, Reg_ESP}, + {"AL", NULL_TREE, NULL, 1, Reg_EAX}, + {"AH", NULL_TREE, NULL, 1, Reg_EAX}, + {"BL", NULL_TREE, NULL, 1, Reg_EBX}, + {"BH", NULL_TREE, NULL, 1, Reg_EBX}, + {"CL", NULL_TREE, NULL, 1, Reg_ECX}, + {"CH", NULL_TREE, NULL, 1, Reg_ECX}, + {"DL", NULL_TREE, NULL, 1, Reg_EDX}, + {"DH", NULL_TREE, NULL, 1, Reg_EDX}, + {"CR0", NULL_TREE, NULL, 0, -1}, + {"CR2", NULL_TREE, NULL, 0, -1}, + {"CR3", NULL_TREE, NULL, 0, -1}, + {"CR4", NULL_TREE, NULL, 0, -1}, + {"DR0", NULL_TREE, NULL, 0, -1}, + {"DR1", NULL_TREE, NULL, 0, -1}, + {"DR2", NULL_TREE, NULL, 0, -1}, + {"DR3", NULL_TREE, NULL, 0, -1}, + {"DR6", NULL_TREE, NULL, 0, -1}, + {"DR7", NULL_TREE, NULL, 0, -1}, + {"TR3", NULL_TREE, NULL, 0, -1}, + {"TR4", NULL_TREE, NULL, 0, -1}, + {"TR5", NULL_TREE, NULL, 0, -1}, + {"TR6", NULL_TREE, NULL, 0, -1}, + {"TR7", NULL_TREE, NULL, 0, -1}}; - typedef enum - { - No_Type_Needed, - Int_Types, - Word_Types, // same as Int_Types, but byte is not allowed - FP_Types, - FPInt_Types, - Byte_NoType, // byte only, but no type suffix - } TypeNeeded; +typedef enum { + No_Type_Needed, + Int_Types, + Word_Types, // same as Int_Types, but byte is not allowed + FP_Types, + FPInt_Types, + Byte_NoType, // byte only, but no type suffix +} TypeNeeded; - typedef enum - { - No_Link, - Out_Mnemonic, - Next_Form - } OpLink; +typedef enum { No_Link, Out_Mnemonic, Next_Form } OpLink; - typedef enum - { - Clb_SizeAX = 0x01, - Clb_SizeDXAX = 0x02, - Clb_EAX = 0x03, +typedef enum { + Clb_SizeAX = 0x01, + Clb_SizeDXAX = 0x02, + Clb_EAX = 0x03, #ifndef ASM_X86_64 - Clb_DXAX_Mask = 0x03, + Clb_DXAX_Mask = 0x03, #else - Clb_DXAX_Mask = 0x103, - Clb_SizeRDXRAX = 0x100, + Clb_DXAX_Mask = 0x103, + Clb_SizeRDXRAX = 0x100, #endif - Clb_Flags = 0x04, - Clb_DI = 0x08, - Clb_SI = 0x10, - Clb_CX = 0x20, - Clb_ST = 0x40, - Clb_SP = 0x80 // Doesn't actually let GCC know the frame pointer is modified - } ImplicitClober; + Clb_Flags = 0x04, + Clb_DI = 0x08, + Clb_SI = 0x10, + Clb_CX = 0x20, + Clb_ST = 0x40, + Clb_SP = 0x80 // Doesn't actually let GCC know the frame pointer is modified +} ImplicitClober; // "^ +/..\([A-Za-z_0-9]+\).*" -> " \1," - typedef enum - { - Op_Invalid, - Op_Adjust, - Op_Dst, - Op_Upd, - Op_DstW, - Op_DstF, - Op_UpdF, - Op_DstSrc, - Op_DstSrcF, - Op_UpdSrcF, - Op_DstSrcFW, - Op_UpdSrcFW, - Op_DstSrcSSE, - Op_DstSrcMMX, - Op_DstSrcImmS, - Op_DstSrcImmM, - Op_ExtSrcImmS, - Op_UpdSrcShft, - Op_DstSrcNT, - Op_UpdSrcNT, - Op_DstMemNT, - Op_DstRMBNT, - Op_DstRMWNT, - Op_UpdUpd, - Op_UpdUpdF, - Op_Src, - Op_SrcRMWNT, - Op_SrcW, - Op_SrcImm, - Op_Src_DXAXF, - Op_SrcMemNT, - Op_SrcMemNTF, - Op_SrcSrc, - Op_SrcSrcF, - Op_SrcSrcFW, - Op_SrcSrcSSEF, - Op_SrcSrcMMX, - Op_Shift, - Op_Branch, - Op_CBranch, - Op_0, - Op_0_AX, - Op_0_DXAX, - Op_Loop, - Op_Flags, - Op_F0_ST, - Op_F0_P, - Op_Fs_P, - Op_Fis, - Op_Fis_ST, - Op_Fis_P, - Op_Fid, - Op_Fid_P, - Op_FidR_P, - Op_Ffd, - Op_FfdR, - Op_Ffd_P, - Op_FfdR_P, - Op_FfdRR_P, - Op_Fd_P, - Op_FdST, - Op_FMath, - Op_FMath0, - Op_FMath2, - Op_FdSTiSTi, - Op_FdST0ST1, - Op_FPMath, - Op_FCmp, - Op_FCmp1, - Op_FCmpP, - Op_FCmpP1, - Op_FCmpFlg, - Op_FCmpFlgP, - Op_fld, - Op_fldR, - Op_fxch, - Op_fxch1, - Op_fxch0, - Op_SizedStack, - Op_bound, - Op_bswap, - Op_cmps, - Op_cmpsd, - Op_cmpsX, - Op_cmpxchg, +typedef enum { + Op_Invalid, + Op_Adjust, + Op_Dst, + Op_Upd, + Op_DstW, + Op_DstF, + Op_UpdF, + Op_DstSrc, + Op_DstSrcF, + Op_UpdSrcF, + Op_DstSrcFW, + Op_UpdSrcFW, + Op_DstSrcSSE, + Op_DstSrcMMX, + Op_DstSrcImmS, + Op_DstSrcImmM, + Op_ExtSrcImmS, + Op_UpdSrcShft, + Op_DstSrcNT, + Op_UpdSrcNT, + Op_DstMemNT, + Op_DstRMBNT, + Op_DstRMWNT, + Op_UpdUpd, + Op_UpdUpdF, + Op_Src, + Op_SrcRMWNT, + Op_SrcW, + Op_SrcImm, + Op_Src_DXAXF, + Op_SrcMemNT, + Op_SrcMemNTF, + Op_SrcSrc, + Op_SrcSrcF, + Op_SrcSrcFW, + Op_SrcSrcSSEF, + Op_SrcSrcMMX, + Op_Shift, + Op_Branch, + Op_CBranch, + Op_0, + Op_0_AX, + Op_0_DXAX, + Op_Loop, + Op_Flags, + Op_F0_ST, + Op_F0_P, + Op_Fs_P, + Op_Fis, + Op_Fis_ST, + Op_Fis_P, + Op_Fid, + Op_Fid_P, + Op_FidR_P, + Op_Ffd, + Op_FfdR, + Op_Ffd_P, + Op_FfdR_P, + Op_FfdRR_P, + Op_Fd_P, + Op_FdST, + Op_FMath, + Op_FMath0, + Op_FMath2, + Op_FdSTiSTi, + Op_FdST0ST1, + Op_FPMath, + Op_FCmp, + Op_FCmp1, + Op_FCmpP, + Op_FCmpP1, + Op_FCmpFlg, + Op_FCmpFlgP, + Op_fld, + Op_fldR, + Op_fxch, + Op_fxch1, + Op_fxch0, + Op_SizedStack, + Op_bound, + Op_bswap, + Op_cmps, + Op_cmpsd, + Op_cmpsX, + Op_cmpxchg, #ifdef ASM_X86_64 - Op_cmpxchg16b, + Op_cmpxchg16b, #endif - Op_cmpxchg8b, - Op_cpuid, - Op_enter, - Op_fdisi, - Op_feni, - Op_fsetpm, - Op_fXstsw, - Op_imul, - Op_imul2, - Op_imul1, - Op_in, - Op_ins, - Op_insX, - Op_iret, - Op_iretd, + Op_cmpxchg8b, + Op_cpuid, + Op_enter, + Op_fdisi, + Op_feni, + Op_fsetpm, + Op_fXstsw, + Op_imul, + Op_imul2, + Op_imul1, + Op_in, + Op_ins, + Op_insX, + Op_iret, + Op_iretd, #ifdef ASM_X86_64 - Op_iretq, + Op_iretq, #endif - Op_lods, - Op_lodsX, - Op_movs, - Op_movsd, - Op_movsX, - Op_movsx, - Op_movzx, - Op_mul, - Op_out, - Op_outs, - Op_outsX, - Op_push, - Op_ret, - Op_retf, - Op_scas, - Op_scasX, - Op_stos, - Op_stosX, - Op_xgetbv, - Op_xlat, - N_AsmOpInfo, - Op_Align, - Op_Even, - Op_Naked, - Op_db, - Op_ds, - Op_di, - Op_dl, - Op_df, - Op_dd, - Op_de - } AsmOp; + Op_lods, + Op_lodsX, + Op_movs, + Op_movsd, + Op_movsX, + Op_movsx, + Op_movzx, + Op_mul, + Op_out, + Op_outs, + Op_outsX, + Op_push, + Op_ret, + Op_retf, + Op_scas, + Op_scasX, + Op_stos, + Op_stosX, + Op_xgetbv, + Op_xlat, + N_AsmOpInfo, + Op_Align, + Op_Even, + Op_Naked, + Op_db, + Op_ds, + Op_di, + Op_dl, + Op_df, + Op_dd, + Op_de +} AsmOp; - typedef enum - { - Opr_None = 0, - OprC_MRI = 1, - OprC_MR = 2, - OprC_Mem = 3, - OprC_Reg = 4, - OprC_Imm = 5, - OprC_SSE = 6, - OprC_SSE_Mem = 7, - OprC_R32 = 8, - OprC_RWord = 9, - OprC_RFP = 10, - OprC_AbsRel = 11, - OprC_Relative = 12, - OprC_Port = 13, // DX or imm - OprC_AX = 14, // AL,AX,EAX - OprC_DX = 15, // only DX - OprC_MMX = 16, - OprC_MMX_Mem = 17, - OprC_Shift = 18, // imm or CL +typedef enum { + Opr_None = 0, + OprC_MRI = 1, + OprC_MR = 2, + OprC_Mem = 3, + OprC_Reg = 4, + OprC_Imm = 5, + OprC_SSE = 6, + OprC_SSE_Mem = 7, + OprC_R32 = 8, + OprC_RWord = 9, + OprC_RFP = 10, + OprC_AbsRel = 11, + OprC_Relative = 12, + OprC_Port = 13, // DX or imm + OprC_AX = 14, // AL,AX,EAX + OprC_DX = 15, // only DX + OprC_MMX = 16, + OprC_MMX_Mem = 17, + OprC_Shift = 18, // imm or CL - Opr_ClassMask = 0x1f, + Opr_ClassMask = 0x1f, - Opr_Dest = 0x20, - Opr_Update = 0x60, + Opr_Dest = 0x20, + Opr_Update = 0x60, - Opr_NoType = 0x80, - } OprVals; + Opr_NoType = 0x80, +} OprVals; +typedef struct { +} AsmOprInfo; - typedef struct - { - } AsmOprInfo; +typedef unsigned char Opr; - typedef unsigned char Opr; - - typedef struct - { - Opr operands[3]; +typedef struct { + Opr operands[3]; #ifndef ASM_X86_64 - unsigned char - needsType : 3, - implicitClobbers : 8, - linkType : 2; + unsigned char needsType : 3, implicitClobbers : 8, linkType : 2; #else - unsigned short - needsType : 3, - implicitClobbers : 9, - linkType : 2; + unsigned short needsType : 3, implicitClobbers : 9, linkType : 2; #endif - unsigned link; + unsigned link; - unsigned nOperands() - { - if ( !operands[0] ) - return 0; - else if ( !operands[1] ) - return 1; - else if ( !operands[2] ) - return 2; - else - return 3; - } - } AsmOpInfo; + unsigned nOperands() { + if (!operands[0]) + return 0; + else if (!operands[1]) + return 1; + else if (!operands[2]) + return 2; + else + return 3; + } +} AsmOpInfo; - typedef enum - { - Mn_fdisi, - Mn_feni, - Mn_fsetpm, - Mn_iretw, - Mn_iret, +typedef enum { + Mn_fdisi, + Mn_feni, + Mn_fsetpm, + Mn_iretw, + Mn_iret, #ifdef ASM_X86_64 - Mn_iretq, + Mn_iretq, #endif - Mn_lret, - Mn_cmpxchg8b, + Mn_lret, + Mn_cmpxchg8b, #ifdef ASM_X86_64 - Mn_cmpxchg16b, + Mn_cmpxchg16b, #endif - N_AltMn - } Alternate_Mnemonics; + N_AltMn +} Alternate_Mnemonics; - static const char * alternateMnemonics[N_AltMn] = - { - ".byte 0xdb, 0xe1", - ".byte 0xdb, 0xe0", - ".byte 0xdb, 0xe4", - "iretw", - "iret", +static const char *alternateMnemonics[N_AltMn] = { + ".byte 0xdb, 0xe1", + ".byte 0xdb, 0xe0", + ".byte 0xdb, 0xe4", + "iretw", + "iret", #ifdef ASM_X86_64 - "iretq", + "iretq", #endif - "lret", - "cmpxchg8b", + "lret", + "cmpxchg8b", #ifdef ASM_X86_64 - "cmpxchg16b", + "cmpxchg16b", #endif - }; +}; -#define mri OprC_MRI -#define mr OprC_MR -#define mem OprC_Mem +#define mri OprC_MRI +#define mr OprC_MR +#define mem OprC_Mem // for now mfp=mem -#define mfp OprC_Mem -#define reg OprC_Reg -#define imm OprC_Imm -#define sse OprC_SSE +#define mfp OprC_Mem +#define reg OprC_Reg +#define imm OprC_Imm +#define sse OprC_SSE #define ssem OprC_SSE_Mem -#define mmx OprC_MMX +#define mmx OprC_MMX #define mmxm OprC_MMX_Mem -#define r32 OprC_R32 -#define rw OprC_RWord -#define rfp OprC_RFP +#define r32 OprC_R32 +#define rw OprC_RWord +#define rfp OprC_RFP #define port OprC_Port -#define ax OprC_AX -#define dx OprC_DX +#define ax OprC_AX +#define dx OprC_DX #define shft OprC_Shift -#define D Opr_Dest -#define U Opr_Update -#define N Opr_NoType +#define D Opr_Dest +#define U Opr_Update +#define N Opr_NoType // D=dest, N=notype - static AsmOpInfo asmOpInfo[N_AsmOpInfo] = - { - /* Op_Invalid */ {}, - /* Op_Adjust */ { { 0, 0, 0 }, 0, Clb_EAX /*just AX*/ }, - /* Op_Dst */ { { D|mr, 0, 0 }, 1 }, - /* Op_Upd */ { { U|mr, 0, 0 }, 1 }, - /* Op_DstW */ { { D|mr, 0, 0 }, Word_Types }, - /* Op_DstF */ { { D|mr, 0, 0 }, 1, Clb_Flags }, - /* Op_UpdF */ { { U|mr, 0, 0 }, 1, Clb_Flags }, - /* Op_DstSrc */ { { D|mr, mri, 0 },/**/1 }, - /* Op_DstSrcF */ { { D|mr, mri, 0 },/**/1, Clb_Flags }, - /* Op_UpdSrcF */ { { U|mr, mri, 0 },/**/1, Clb_Flags }, - /* Op_DstSrcFW */ { { D|mr, mri, 0 },/**/Word_Types, Clb_Flags }, - /* Op_UpdSrcFW */ { { U|mr, mri, 0 },/**/Word_Types, Clb_Flags }, - /* Op_DstSrcSSE */ { { U|sse, ssem, 0 } }, // some may not be update %% - /* Op_DstSrcMMX */ { { U|mmx, mmxm, 0 } }, // some may not be update %% - /* Op_DstSrcImmS*/ { { U|sse, ssem, N|imm } }, // some may not be update %% - /* Op_DstSrcImmM*/ { { U|mmx, mmxm, N|imm } }, // some may not be update %% - /* Op_ExtSrcImmS*/ { { D|mr, sse, N|imm } }, // used for extractps - /* Op_UpdSrcShft*/ { { U|mr, reg, N|shft},1, Clb_Flags }, // 16/32 only - /* Op_DstSrcNT */ { { D|mr, mr, 0 }, 0 }, // used for movd .. operands can be rm32,sse,mmx - /* Op_UpdSrcNT */ { { U|mr, mr, 0 }, 0 }, // used for movd .. operands can be rm32,sse,mmx - /* Op_DstMemNT */ { { D|mem, 0, 0 } }, - /* Op_DstRMBNT */ { { D|mr, 0, 0 }, Byte_NoType }, - /* Op_DstRMWNT */ { { D|mr, 0, 0 } }, - /* Op_UpdUpd */ { { U|mr,U|mr, 0 },/**/1 }, - /* Op_UpdUpdF */ { { U|mr,U|mr, 0 },/**/1, Clb_Flags }, - /* Op_Src */ { { mri, 0, 0 }, 1 }, - /* Op_SrcRMWNT */ { { mr, 0, 0 }, 0 }, - /* Op_SrcW */ { { mri, 0, 0 }, Word_Types }, - /* Op_SrcImm */ { { imm, 0, 0 } }, - /* Op_Src_DXAXF */ { { mr, 0, 0 }, 1, Clb_SizeDXAX|Clb_Flags }, - /* Op_SrcMemNT */ { { mem, 0, 0 } }, - /* Op_SrcMemNTF */ { { mem, 0, 0 }, 0, Clb_Flags }, - /* Op_SrcSrc */ { { mr, mri, 0 }, 1 }, - /* Op_SrcSrcF */ { { mr, mri, 0 }, 1, Clb_Flags }, - /* Op_SrcSrcFW */ { { mr, mri, 0 }, Word_Types, Clb_Flags }, - /* Op_SrcSrcSSEF*/ { { sse, ssem, 0 }, 0, Clb_Flags }, - /* Op_SrcSrcMMX */ { { mmx, mmx, 0 } }, - /* Op_Shift */ { { D|mr,N|shft, 0 },/**/1, Clb_Flags }, - /* Op_Branch */ { { mri, 0, 0 } }, - /* Op_CBranch */ { { imm, 0, 0 } }, - /* Op_0 */ { { 0, 0, 0 } }, - /* Op_0_AX */ { { 0, 0, 0 }, 0, Clb_SizeAX }, - /* Op_0_DXAX */ { { 0, 0, 0 }, 0, Clb_SizeDXAX }, // but for cwd/cdq -- how do know the size.. - /* Op_Loop */ { { imm, 0, 0 }, 0, Clb_CX }, - /* Op_Flags */ { { 0, 0, 0 }, 0, Clb_Flags }, - /* Op_F0_ST */ { { 0, 0, 0 }, 0, Clb_ST }, - /* Op_F0_P */ { { 0, 0, 0 }, 0, Clb_ST }, // push, pops, etc. not sure how to inform gcc.. - /* Op_Fs_P */ { { mem, 0, 0 }, 0, Clb_ST }, // " - /* Op_Fis */ { { mem, 0, 0 }, FPInt_Types }, // only 16bit and 32bit, DMD defaults to 16bit - /* Op_Fis_ST */ { { mem, 0, 0 }, FPInt_Types, Clb_ST }, // " - /* Op_Fis_P */ { { mem, 0, 0 }, FPInt_Types, Clb_ST }, // push and pop, fild so also 64 bit - /* Op_Fid */ { { D|mem, 0, 0 }, FPInt_Types }, // only 16bit and 32bit, DMD defaults to 16bit - /* Op_Fid_P */ { { D|mem, 0, 0 }, FPInt_Types, Clb_ST, Next_Form, Op_FidR_P }, // push and pop, fild so also 64 bit - /* Op_FidR_P */ { { D|mem,rfp, 0 }, FPInt_Types, Clb_ST }, // push and pop, fild so also 64 bit - /* Op_Ffd */ { { D|mfp, 0, 0 }, FP_Types, 0, Next_Form, Op_FfdR }, // only 16bit and 32bit, DMD defaults to 16bit, reg form doesn't need type - /* Op_FfdR */ { { D|rfp, 0, 0 } }, - /* Op_Ffd_P */ { { D|mfp, 0, 0 }, FP_Types, Clb_ST, Next_Form, Op_FfdR_P }, // pop, fld so also 80 bit, " - /* Op_FfdR_P */ { { D|rfp, 0, 0 }, 0, Clb_ST, Next_Form, Op_FfdRR_P }, - /* Op_FfdRR_P */ { { D|rfp, rfp, 0 }, 0, Clb_ST }, - /* Op_Fd_P */ { { D|mem, 0, 0 }, 0, Clb_ST }, // " - /* Op_FdST */ { { D|rfp, 0, 0 } }, - /* Op_FMath */ { { mfp, 0, 0 }, FP_Types, Clb_ST, Next_Form, Op_FMath0 }, // and only single or double prec - /* Op_FMath0 */ { { 0, 0, 0 }, 0, Clb_ST, Next_Form, Op_FMath2 }, // pops - /* Op_FMath2 */ { { D|rfp, rfp, 0 }, 0, Clb_ST, Next_Form, Op_FdST0ST1 }, // and only single or double prec - /* Op_FdSTiSTi */ { { D|rfp, rfp, 0 } }, - /* Op_FdST0ST1 */ { { 0, 0, 0 } }, - /* Op_FPMath */ { { D|rfp, rfp, 0 }, 0, Clb_ST, Next_Form, Op_F0_P }, // pops - /* Op_FCmp */ { { mfp, 0, 0 }, FP_Types, 0, Next_Form, Op_FCmp1 }, // DMD defaults to float ptr - /* Op_FCmp1 */ { { rfp, 0, 0 }, 0, 0, Next_Form, Op_0 }, - /* Op_FCmpP */ { { mfp, 0, 0 }, FP_Types, 0, Next_Form, Op_FCmpP1 }, // pops - /* Op_FCmpP1 */ { { rfp, 0, 0 }, 0, 0, Next_Form, Op_F0_P }, // pops - /* Op_FCmpFlg */ { { rfp, 0, 0 }, 0, Clb_Flags }, - /* Op_FCmpFlgP */ { { rfp, 0, 0 }, 0, Clb_Flags }, // pops - /* Op_fld */ { { mfp, 0, 0 }, FP_Types, Clb_ST, Next_Form, Op_fldR }, - /* Op_fldR */ { { rfp, 0, 0 }, 0, Clb_ST }, - /* Op_fxch */ { { D|rfp,D|rfp, 0 }, 0, Clb_ST, Next_Form, Op_fxch1 }, // not in intel manual?, but DMD allows it (gas won't), second arg must be ST - /* Op_fxch1 */ { { D|rfp, 0, 0 }, 0, Clb_ST, Next_Form, Op_fxch0 }, - /* Op_fxch0 */ { { 0, 0, 0 }, 0, Clb_ST }, // Also clobbers ST(1) - /* Op_SizedStack*/ { { 0, 0, 0 }, 0, Clb_SP }, // type suffix special case - /* Op_bound */ { { mr, mri, 0 }, Word_Types }, // operands *not* reversed for gas - /* Op_bswap */ { { D|r32, 0, 0 } }, - /* Op_cmps */ { { mem, mem, 0 }, 1, Clb_DI|Clb_SI|Clb_Flags }, - /* Op_cmpsd */ { { 0, 0, 0 }, 0, Clb_DI|Clb_SI|Clb_Flags, Next_Form, Op_DstSrcImmS }, - /* Op_cmpsX */ { { 0, 0, 0 }, 0, Clb_DI|Clb_SI|Clb_Flags }, - /* Op_cmpxchg */ { { D|mr, reg, 0 }, 1, Clb_SizeAX|Clb_Flags }, +static AsmOpInfo asmOpInfo[N_AsmOpInfo] = { + /* Op_Invalid */ {}, + /* Op_Adjust */ {{0, 0, 0}, 0, Clb_EAX /*just AX*/}, + /* Op_Dst */ {{D | mr, 0, 0}, 1}, + /* Op_Upd */ {{U | mr, 0, 0}, 1}, + /* Op_DstW */ {{D | mr, 0, 0}, Word_Types}, + /* Op_DstF */ {{D | mr, 0, 0}, 1, Clb_Flags}, + /* Op_UpdF */ {{U | mr, 0, 0}, 1, Clb_Flags}, + /* Op_DstSrc */ {{D | mr, mri, 0}, /**/ 1}, + /* Op_DstSrcF */ {{D | mr, mri, 0}, /**/ 1, Clb_Flags}, + /* Op_UpdSrcF */ {{U | mr, mri, 0}, /**/ 1, Clb_Flags}, + /* Op_DstSrcFW */ {{D | mr, mri, 0}, /**/ Word_Types, Clb_Flags}, + /* Op_UpdSrcFW */ {{U | mr, mri, 0}, /**/ Word_Types, Clb_Flags}, + /* Op_DstSrcSSE */ {{U | sse, ssem, 0}}, // some may not be update %% + /* Op_DstSrcMMX */ {{U | mmx, mmxm, 0}}, // some may not be update %% + /* Op_DstSrcImmS*/ {{U | sse, ssem, N | imm}}, // some may not be update %% + /* Op_DstSrcImmM*/ {{U | mmx, mmxm, N | imm}}, // some may not be update %% + /* Op_ExtSrcImmS*/ {{D | mr, sse, N | imm}}, // used for extractps + /* Op_UpdSrcShft*/ {{U | mr, reg, N | shft}, 1, Clb_Flags}, // 16/32 only + /* Op_DstSrcNT */ {{D | mr, mr, 0}, + 0}, // used for movd .. operands can be rm32,sse,mmx + /* Op_UpdSrcNT */ {{U | mr, mr, 0}, + 0}, // used for movd .. operands can be rm32,sse,mmx + /* Op_DstMemNT */ {{D | mem, 0, 0}}, + /* Op_DstRMBNT */ {{D | mr, 0, 0}, Byte_NoType}, + /* Op_DstRMWNT */ {{D | mr, 0, 0}}, + /* Op_UpdUpd */ {{U | mr, U | mr, 0}, /**/ 1}, + /* Op_UpdUpdF */ {{U | mr, U | mr, 0}, /**/ 1, Clb_Flags}, + /* Op_Src */ {{mri, 0, 0}, 1}, + /* Op_SrcRMWNT */ {{mr, 0, 0}, 0}, + /* Op_SrcW */ {{mri, 0, 0}, Word_Types}, + /* Op_SrcImm */ {{imm, 0, 0}}, + /* Op_Src_DXAXF */ {{mr, 0, 0}, 1, Clb_SizeDXAX | Clb_Flags}, + /* Op_SrcMemNT */ {{mem, 0, 0}}, + /* Op_SrcMemNTF */ {{mem, 0, 0}, 0, Clb_Flags}, + /* Op_SrcSrc */ {{mr, mri, 0}, 1}, + /* Op_SrcSrcF */ {{mr, mri, 0}, 1, Clb_Flags}, + /* Op_SrcSrcFW */ {{mr, mri, 0}, Word_Types, Clb_Flags}, + /* Op_SrcSrcSSEF*/ {{sse, ssem, 0}, 0, Clb_Flags}, + /* Op_SrcSrcMMX */ {{mmx, mmx, 0}}, + /* Op_Shift */ {{D | mr, N | shft, 0}, /**/ 1, Clb_Flags}, + /* Op_Branch */ {{mri, 0, 0}}, + /* Op_CBranch */ {{imm, 0, 0}}, + /* Op_0 */ {{0, 0, 0}}, + /* Op_0_AX */ {{0, 0, 0}, 0, Clb_SizeAX}, + /* Op_0_DXAX */ {{0, 0, 0}, 0, Clb_SizeDXAX}, // but for cwd/cdq -- how + // do know the size.. + /* Op_Loop */ {{imm, 0, 0}, 0, Clb_CX}, + /* Op_Flags */ {{0, 0, 0}, 0, Clb_Flags}, + /* Op_F0_ST */ {{0, 0, 0}, 0, Clb_ST}, + /* Op_F0_P */ {{0, 0, 0}, 0, Clb_ST}, // push, pops, etc. not sure how + // to inform gcc.. + /* Op_Fs_P */ {{mem, 0, 0}, 0, Clb_ST}, // " + /* Op_Fis */ {{mem, 0, 0}, FPInt_Types}, // only 16bit and 32bit, DMD + // defaults to 16bit + /* Op_Fis_ST */ {{mem, 0, 0}, FPInt_Types, Clb_ST}, // " + /* Op_Fis_P */ {{mem, 0, 0}, + FPInt_Types, + Clb_ST}, // push and pop, fild so also 64 bit + /* Op_Fid */ {{D | mem, 0, 0}, FPInt_Types}, // only 16bit and 32bit, + // DMD defaults to 16bit + /* Op_Fid_P */ {{D | mem, 0, 0}, + FPInt_Types, + Clb_ST, + Next_Form, + Op_FidR_P}, // push and pop, fild so also 64 bit + /* Op_FidR_P */ {{D | mem, rfp, 0}, + FPInt_Types, + Clb_ST}, // push and pop, fild so also 64 bit + /* Op_Ffd */ {{D | mfp, 0, 0}, + FP_Types, + 0, + Next_Form, + Op_FfdR}, // only 16bit and 32bit, DMD defaults to + // 16bit, reg form doesn't need type + /* Op_FfdR */ {{D | rfp, 0, 0}}, + /* Op_Ffd_P */ {{D | mfp, 0, 0}, + FP_Types, + Clb_ST, + Next_Form, + Op_FfdR_P}, // pop, fld so also 80 bit, " + /* Op_FfdR_P */ {{D | rfp, 0, 0}, 0, Clb_ST, Next_Form, Op_FfdRR_P}, + /* Op_FfdRR_P */ {{D | rfp, rfp, 0}, 0, Clb_ST}, + /* Op_Fd_P */ {{D | mem, 0, 0}, 0, Clb_ST}, // " + /* Op_FdST */ {{D | rfp, 0, 0}}, + /* Op_FMath */ {{mfp, 0, 0}, + FP_Types, + Clb_ST, + Next_Form, + Op_FMath0}, // and only single or double prec + /* Op_FMath0 */ {{0, 0, 0}, 0, Clb_ST, Next_Form, Op_FMath2}, // pops + /* Op_FMath2 */ {{D | rfp, rfp, 0}, + 0, + Clb_ST, + Next_Form, + Op_FdST0ST1}, // and only single or double prec + /* Op_FdSTiSTi */ {{D | rfp, rfp, 0}}, + /* Op_FdST0ST1 */ {{0, 0, 0}}, + /* Op_FPMath */ {{D | rfp, rfp, 0}, + 0, + Clb_ST, + Next_Form, + Op_F0_P}, // pops + /* Op_FCmp */ {{mfp, 0, 0}, + FP_Types, + 0, + Next_Form, + Op_FCmp1}, // DMD defaults to float ptr + /* Op_FCmp1 */ {{rfp, 0, 0}, 0, 0, Next_Form, Op_0}, + /* Op_FCmpP */ {{mfp, 0, 0}, FP_Types, 0, Next_Form, Op_FCmpP1}, // pops + /* Op_FCmpP1 */ {{rfp, 0, 0}, 0, 0, Next_Form, Op_F0_P}, // pops + /* Op_FCmpFlg */ {{rfp, 0, 0}, 0, Clb_Flags}, + /* Op_FCmpFlgP */ {{rfp, 0, 0}, 0, Clb_Flags}, // pops + /* Op_fld */ {{mfp, 0, 0}, FP_Types, Clb_ST, Next_Form, Op_fldR}, + /* Op_fldR */ {{rfp, 0, 0}, 0, Clb_ST}, + /* Op_fxch */ {{D | rfp, D | rfp, 0}, + 0, + Clb_ST, + Next_Form, + Op_fxch1}, // not in intel manual?, but DMD allows it + // (gas won't), second arg must be ST + /* Op_fxch1 */ {{D | rfp, 0, 0}, 0, Clb_ST, Next_Form, Op_fxch0}, + /* Op_fxch0 */ {{0, 0, 0}, 0, Clb_ST}, // Also clobbers ST(1) + /* Op_SizedStack*/ {{0, 0, 0}, 0, Clb_SP}, // type suffix special case + /* Op_bound */ {{mr, mri, 0}, + Word_Types}, // operands *not* reversed for gas + /* Op_bswap */ {{D | r32, 0, 0}}, + /* Op_cmps */ {{mem, mem, 0}, 1, Clb_DI | Clb_SI | Clb_Flags}, + /* Op_cmpsd */ {{0, 0, 0}, + 0, + Clb_DI | Clb_SI | Clb_Flags, + Next_Form, + Op_DstSrcImmS}, + /* Op_cmpsX */ {{0, 0, 0}, 0, Clb_DI | Clb_SI | Clb_Flags}, + /* Op_cmpxchg */ {{D | mr, reg, 0}, 1, Clb_SizeAX | Clb_Flags}, #ifdef ASM_X86_64 - /* Op_cmpxchg16b */ { { D|mem/*128*/,0, 0 }, 0, Clb_SizeRDXRAX/*64*/|Clb_Flags, Out_Mnemonic, Mn_cmpxchg16b }, + /* Op_cmpxchg16b */ {{D | mem /*128*/, 0, 0}, + 0, + Clb_SizeRDXRAX /*64*/ | Clb_Flags, + Out_Mnemonic, + Mn_cmpxchg16b}, #endif - /* Op_cmpxchg8b */ { { D|mem/*64*/, 0, 0 }, 0, Clb_SizeDXAX/*32*/|Clb_Flags, Out_Mnemonic, Mn_cmpxchg8b }, - /* Op_cpuid */ { { 0, 0, 0 } }, // Clobbers eax, ebx, ecx, and edx. Handled specially below. - /* Op_enter */ { { imm, imm, 0 } }, // operands *not* reversed for gas, %% inform gcc of EBP clobber?, - /* Op_fdisi */ { { 0, 0, 0 }, 0, 0, Out_Mnemonic, Mn_fdisi }, - /* Op_feni */ { { 0, 0, 0 }, 0, 0, Out_Mnemonic, Mn_feni }, - /* Op_fsetpm */ { { 0, 0, 0 }, 0, 0, Out_Mnemonic, Mn_fsetpm }, - /* Op_fXstsw */ { { D|mr, 0, 0 } }, // ax is the only allowed register - /* Op_imul */ { { D|reg, mr, imm }, 1, Clb_Flags, Next_Form, Op_imul2 }, // 16/32 only - /* Op_imul2 */ { { D|reg, mri, 0 }, 1, Clb_Flags, Next_Form, Op_imul1 }, // 16/32 only - /* Op_imul1 */ { { mr, 0, 0 }, 1, Clb_Flags|Clb_SizeDXAX }, - /* Op_in */ { { D|ax,N|port, 0 }, 1 }, - /* Op_ins */ { { mem,N|dx, 0 }, 1, Clb_DI }, // can't override ES segment for this one - /* Op_insX */ { { 0, 0, 0 }, 0, Clb_DI }, // output segment overrides %% needs work - /* Op_iret */ { { 0, 0, 0 }, 0, 0, Out_Mnemonic, Mn_iretw }, - /* Op_iretd */ { { 0, 0, 0 }, 0, 0, Out_Mnemonic, Mn_iret }, + /* Op_cmpxchg8b */ {{D | mem /*64*/, 0, 0}, + 0, + Clb_SizeDXAX /*32*/ | Clb_Flags, + Out_Mnemonic, + Mn_cmpxchg8b}, + /* Op_cpuid */ {{0, 0, 0}}, // Clobbers eax, ebx, ecx, and edx. Handled + // specially below. + /* Op_enter */ {{imm, imm, 0}}, // operands *not* reversed for gas, %% + // inform gcc of EBP clobber?, + /* Op_fdisi */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_fdisi}, + /* Op_feni */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_feni}, + /* Op_fsetpm */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_fsetpm}, + /* Op_fXstsw */ {{D | mr, 0, 0}}, // ax is the only allowed register + /* Op_imul */ {{D | reg, mr, imm}, + 1, + Clb_Flags, + Next_Form, + Op_imul2}, // 16/32 only + /* Op_imul2 */ {{D | reg, mri, 0}, + 1, + Clb_Flags, + Next_Form, + Op_imul1}, // 16/32 only + /* Op_imul1 */ {{mr, 0, 0}, 1, Clb_Flags | Clb_SizeDXAX}, + /* Op_in */ {{D | ax, N | port, 0}, 1}, + /* Op_ins */ {{mem, N | dx, 0}, + 1, + Clb_DI}, // can't override ES segment for this one + /* Op_insX */ {{0, 0, 0}, + 0, + Clb_DI}, // output segment overrides %% needs work + /* Op_iret */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_iretw}, + /* Op_iretd */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_iret}, #ifdef ASM_X86_64 - /* Op_iretq */ { { 0, 0, 0 }, 0, 0, Out_Mnemonic, Mn_iretq }, + /* Op_iretq */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_iretq}, #endif - /* Op_lods */ { { mem, 0, 0 }, 1, Clb_SI }, - /* Op_lodsX */ { { 0, 0, 0 }, 0, Clb_SI }, - /* Op_movs */ { { mem, mem, 0 }, 1, Clb_DI|Clb_SI }, // only src/DS can be overridden - /* Op_movsd */ { { 0, 0, 0 }, 0, Clb_DI|Clb_SI, Next_Form, Op_DstSrcSSE }, // %% gas doesn't accept movsd .. has to movsl - /* Op_movsX */ { { 0, 0, 0 }, 0, Clb_DI|Clb_SI }, - /* Op_movsx */ { { D|reg, mr, 0 }, 1 }, // type suffix is special case - /* Op_movzx */ { { D|reg, mr, 0 }, 1 }, // type suffix is special case - /* Op_mul */ { { U|ax, mr, 0 }, 1, Clb_SizeDXAX|Clb_Flags, Next_Form, Op_Src_DXAXF }, - /* Op_out */ { { N|port,ax, 0 }, 1 }, - /* Op_outs */ { { N|dx, mem, 0 }, 1, Clb_SI }, - /* Op_outsX */ { { 0, 0, 0 }, 0, Clb_SI }, + /* Op_lods */ {{mem, 0, 0}, 1, Clb_SI}, + /* Op_lodsX */ {{0, 0, 0}, 0, Clb_SI}, + /* Op_movs */ {{mem, mem, 0}, + 1, + Clb_DI | Clb_SI}, // only src/DS can be overridden + /* Op_movsd */ {{0, 0, 0}, + 0, + Clb_DI | Clb_SI, + Next_Form, + Op_DstSrcSSE}, // %% gas doesn't accept movsd .. has to + // movsl + /* Op_movsX */ {{0, 0, 0}, 0, Clb_DI | Clb_SI}, + /* Op_movsx */ {{D | reg, mr, 0}, 1}, // type suffix is special case + /* Op_movzx */ {{D | reg, mr, 0}, 1}, // type suffix is special case + /* Op_mul */ {{U | ax, mr, 0}, + 1, + Clb_SizeDXAX | Clb_Flags, + Next_Form, + Op_Src_DXAXF}, + /* Op_out */ {{N | port, ax, 0}, 1}, + /* Op_outs */ {{N | dx, mem, 0}, 1, Clb_SI}, + /* Op_outsX */ {{0, 0, 0}, 0, Clb_SI}, #ifndef ASM_X86_64 - /* Op_push */ { { mri, 0, 0 }, Word_Types, Clb_SP }, // would be Op_SrcW, but DMD defaults to 32-bit for immediate form + /* Op_push */ {{mri, 0, 0}, Word_Types, Clb_SP}, // would be Op_SrcW, + // but DMD defaults to + // 32-bit for + // immediate form #else - /* Op_push */ { { mri, 0, 0 }, 0, Clb_SP }, // would be Op_SrcW, but DMD defaults to 32-bit for immediate form + /* Op_push */ {{mri, 0, 0}, 0, Clb_SP}, // would be Op_SrcW, but DMD + // defaults to 32-bit for + // immediate form #endif - /* Op_ret */ { { imm, 0, 0 }, 0, 0, Next_Form, Op_0 }, - /* Op_retf */ { { 0, 0, 0 }, 0, 0, Out_Mnemonic, Mn_lret }, - /* Op_scas */ { { mem, 0, 0 }, 1, Clb_DI|Clb_Flags }, - /* Op_scasX */ { { 0, 0, 0 }, 0, Clb_DI|Clb_Flags }, - /* Op_stos */ { { mem, 0, 0 }, 1, Clb_DI }, - /* Op_stosX */ { { 0, 0, 0 }, 0, Clb_DI }, + /* Op_ret */ {{imm, 0, 0}, 0, 0, Next_Form, Op_0}, + /* Op_retf */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_lret}, + /* Op_scas */ {{mem, 0, 0}, 1, Clb_DI | Clb_Flags}, + /* Op_scasX */ {{0, 0, 0}, 0, Clb_DI | Clb_Flags}, + /* Op_stos */ {{mem, 0, 0}, 1, Clb_DI}, + /* Op_stosX */ {{0, 0, 0}, 0, Clb_DI}, #ifndef ASM_X86_64 - /* Op_xgetbv */ { { 0, 0, 0 }, 0, Clb_SizeDXAX }, + /* Op_xgetbv */ {{0, 0, 0}, 0, Clb_SizeDXAX}, #else - /* Op_xgetbv */ { { 0, 0, 0 }, 0, Clb_SizeRDXRAX }, + /* Op_xgetbv */ {{0, 0, 0}, 0, Clb_SizeRDXRAX}, #endif - /* Op_xlat */ { { mem, 0, 0 }, 0, Clb_SizeAX } + /* Op_xlat */ {{mem, 0, 0}, 0, Clb_SizeAX} - /// * Op_arpl */ { D|mr, reg }, // 16 only -> DstSrc - /// * Op_bsX */ { rw, mrw, 0, 1, Clb_Flags },//->srcsrcf - /// * Op_bt */ { mrw, riw, 0, 1, Clb_Flags },//->srcsrcf - /// * Op_btX */ { D|mrw, riw, 0, 1, Clb_Flags },//->dstsrcf .. immediate does not contribute to size - /// * Op_cmovCC */ { D|rw, mrw, 0, 1 } // ->dstsrc - }; + /// * Op_arpl */ { D|mr, reg }, // 16 only -> DstSrc + /// * Op_bsX */ { rw, mrw, 0, 1, Clb_Flags },//->srcsrcf + /// * Op_bt */ { mrw, riw, 0, 1, Clb_Flags },//->srcsrcf + /// * Op_btX */ { D|mrw, riw, 0, 1, Clb_Flags },//->dstsrcf .. + /// immediate does not contribute to size + /// * Op_cmovCC */ { D|rw, mrw, 0, 1 } // ->dstsrc +}; #undef mri #undef mr @@ -692,853 +848,855 @@ namespace AsmParserx8664 #undef U #undef N - typedef struct - { - const char * inMnemonic; - AsmOp asmOp; - } AsmOpEnt; +typedef struct { + const char *inMnemonic; + AsmOp asmOp; +} AsmOpEnt; - /* Some opcodes have data size restrictions, which we don't check +/* Some opcodes have data size restrictions, which we don't check - cmov, l ?, lea, lsl, shld + cmov, l ?, lea, lsl, shld - todo: push is always the 32-bit form, even tho push is 16-bit - */ + todo: push is always the 32-bit form, even tho push is + 16-bit +*/ - // WARNING: This table is expected to be SORTED ALPHABETICALLY. - static AsmOpEnt opData[] = - { +// WARNING: This table is expected to be SORTED ALPHABETICALLY. +static AsmOpEnt opData[] = { #ifndef ASM_X86_64 - { "aaa", Op_Adjust }, - { "aad", Op_Adjust }, - { "aam", Op_Adjust }, - { "aas", Op_Adjust }, + {"aaa", Op_Adjust}, + {"aad", Op_Adjust}, + {"aam", Op_Adjust}, + {"aas", Op_Adjust}, #endif - { "adc", Op_UpdSrcF }, - { "add", Op_UpdSrcF }, - { "addpd", Op_DstSrcSSE }, - { "addps", Op_DstSrcSSE }, + {"adc", Op_UpdSrcF}, + {"add", Op_UpdSrcF}, + {"addpd", Op_DstSrcSSE}, + {"addps", Op_DstSrcSSE}, #ifdef ASM_X86_64 - { "addq", Op_DstSrcSSE }, // ? + {"addq", Op_DstSrcSSE}, // ? #endif - { "addsd", Op_DstSrcSSE }, - { "addss", Op_DstSrcSSE }, - { "addsubpd", Op_DstSrcSSE }, - { "addsubps", Op_DstSrcSSE }, - { "aesdec", Op_DstSrcSSE }, - { "aesdeclast", Op_DstSrcSSE }, - { "aesenc", Op_DstSrcSSE }, - { "aesenclast", Op_DstSrcSSE }, - { "aesimc", Op_DstSrcSSE }, - { "aeskeygenassist", Op_DstSrcImmS }, + {"addsd", Op_DstSrcSSE}, + {"addss", Op_DstSrcSSE}, + {"addsubpd", Op_DstSrcSSE}, + {"addsubps", Op_DstSrcSSE}, + {"aesdec", Op_DstSrcSSE}, + {"aesdeclast", Op_DstSrcSSE}, + {"aesenc", Op_DstSrcSSE}, + {"aesenclast", Op_DstSrcSSE}, + {"aesimc", Op_DstSrcSSE}, + {"aeskeygenassist", Op_DstSrcImmS}, #ifndef ASM_X86_64 - { "align", Op_Align }, + {"align", Op_Align}, #endif - { "and", Op_UpdSrcF }, - { "andnpd", Op_DstSrcSSE }, - { "andnps", Op_DstSrcSSE }, - { "andpd", Op_DstSrcSSE }, - { "andps", Op_DstSrcSSE }, + {"and", Op_UpdSrcF}, + {"andnpd", Op_DstSrcSSE}, + {"andnps", Op_DstSrcSSE}, + {"andpd", Op_DstSrcSSE}, + {"andps", Op_DstSrcSSE}, #ifndef ASM_X86_64 - { "arpl", Op_UpdSrcNT }, + {"arpl", Op_UpdSrcNT}, #endif - { "blendpd", Op_DstSrcImmS }, - { "blendps", Op_DstSrcImmS }, - { "blendvpd", Op_DstSrcSSE }, - { "blendvps", Op_DstSrcSSE }, - { "bound", Op_bound }, - { "bsf", Op_SrcSrcFW }, - { "bsr", Op_SrcSrcFW }, - { "bswap", Op_bswap }, - { "bt", Op_SrcSrcFW }, - { "btc", Op_UpdSrcFW }, - { "btr", Op_UpdSrcFW }, - { "bts", Op_UpdSrcFW }, - { "call", Op_Branch }, + {"blendpd", Op_DstSrcImmS}, + {"blendps", Op_DstSrcImmS}, + {"blendvpd", Op_DstSrcSSE}, + {"blendvps", Op_DstSrcSSE}, + {"bound", Op_bound}, + {"bsf", Op_SrcSrcFW}, + {"bsr", Op_SrcSrcFW}, + {"bswap", Op_bswap}, + {"bt", Op_SrcSrcFW}, + {"btc", Op_UpdSrcFW}, + {"btr", Op_UpdSrcFW}, + {"bts", Op_UpdSrcFW}, + {"call", Op_Branch}, #ifdef ASM_X86_64 - { "callf", Op_Branch }, // ? + {"callf", Op_Branch}, // ? #endif - { "cbw", Op_0_AX }, + {"cbw", Op_0_AX}, #ifndef ASM_X86_64 - { "cdq", Op_0_DXAX }, + {"cdq", Op_0_DXAX}, #else - { "cdqe", Op_0_DXAX }, + {"cdqe", Op_0_DXAX}, #endif - { "clc", Op_Flags }, - { "cld", Op_Flags }, - { "clflush", Op_SrcMemNT }, - { "cli", Op_Flags }, - { "clts", Op_0 }, - { "cmc", Op_Flags }, - { "cmova", Op_DstSrc }, - { "cmovae", Op_DstSrc }, - { "cmovb", Op_DstSrc }, - { "cmovbe", Op_DstSrc }, - { "cmovc", Op_DstSrc }, - { "cmove", Op_DstSrc }, - { "cmovg", Op_DstSrc }, - { "cmovge", Op_DstSrc }, - { "cmovl", Op_DstSrc }, - { "cmovle", Op_DstSrc }, - { "cmovna", Op_DstSrc }, - { "cmovnae", Op_DstSrc }, - { "cmovnb", Op_DstSrc }, - { "cmovnbe", Op_DstSrc }, - { "cmovnc", Op_DstSrc }, - { "cmovne", Op_DstSrc }, - { "cmovng", Op_DstSrc }, - { "cmovnge", Op_DstSrc }, - { "cmovnl", Op_DstSrc }, - { "cmovnle", Op_DstSrc }, - { "cmovno", Op_DstSrc }, - { "cmovnp", Op_DstSrc }, - { "cmovns", Op_DstSrc }, - { "cmovnz", Op_DstSrc }, - { "cmovo", Op_DstSrc }, - { "cmovp", Op_DstSrc }, - { "cmovpe", Op_DstSrc }, - { "cmovpo", Op_DstSrc }, - { "cmovs", Op_DstSrc }, - { "cmovz", Op_DstSrc }, - { "cmp", Op_SrcSrcF }, - { "cmppd", Op_DstSrcImmS }, - { "cmpps", Op_DstSrcImmS }, + {"clc", Op_Flags}, + {"cld", Op_Flags}, + {"clflush", Op_SrcMemNT}, + {"cli", Op_Flags}, + {"clts", Op_0}, + {"cmc", Op_Flags}, + {"cmova", Op_DstSrc}, + {"cmovae", Op_DstSrc}, + {"cmovb", Op_DstSrc}, + {"cmovbe", Op_DstSrc}, + {"cmovc", Op_DstSrc}, + {"cmove", Op_DstSrc}, + {"cmovg", Op_DstSrc}, + {"cmovge", Op_DstSrc}, + {"cmovl", Op_DstSrc}, + {"cmovle", Op_DstSrc}, + {"cmovna", Op_DstSrc}, + {"cmovnae", Op_DstSrc}, + {"cmovnb", Op_DstSrc}, + {"cmovnbe", Op_DstSrc}, + {"cmovnc", Op_DstSrc}, + {"cmovne", Op_DstSrc}, + {"cmovng", Op_DstSrc}, + {"cmovnge", Op_DstSrc}, + {"cmovnl", Op_DstSrc}, + {"cmovnle", Op_DstSrc}, + {"cmovno", Op_DstSrc}, + {"cmovnp", Op_DstSrc}, + {"cmovns", Op_DstSrc}, + {"cmovnz", Op_DstSrc}, + {"cmovo", Op_DstSrc}, + {"cmovp", Op_DstSrc}, + {"cmovpe", Op_DstSrc}, + {"cmovpo", Op_DstSrc}, + {"cmovs", Op_DstSrc}, + {"cmovz", Op_DstSrc}, + {"cmp", Op_SrcSrcF}, + {"cmppd", Op_DstSrcImmS}, + {"cmpps", Op_DstSrcImmS}, #ifdef ASM_X86_64 - { "cmpq", Op_DstSrcNT }, // ? + {"cmpq", Op_DstSrcNT}, // ? #endif - { "cmps", Op_cmps }, - { "cmpsb", Op_cmpsX }, - { "cmpsd", Op_cmpsd }, // string cmp, and SSE cmp + {"cmps", Op_cmps}, + {"cmpsb", Op_cmpsX}, + {"cmpsd", Op_cmpsd}, // string cmp, and SSE cmp #ifdef ASM_X86_64 - { "cmpsq", Op_cmpsX }, + {"cmpsq", Op_cmpsX}, #endif - { "cmpss", Op_DstSrcImmS }, - { "cmpsw", Op_cmpsX }, - { "cmpxchg", Op_cmpxchg }, + {"cmpss", Op_DstSrcImmS}, + {"cmpsw", Op_cmpsX}, + {"cmpxchg", Op_cmpxchg}, #ifdef ASM_X86_64 - { "cmpxchg16b", Op_cmpxchg16b }, + {"cmpxchg16b", Op_cmpxchg16b}, #endif - { "cmpxchg8b", Op_cmpxchg8b }, - { "comisd", Op_SrcSrcSSEF }, - { "comiss", Op_SrcSrcSSEF }, - { "cpuid", Op_cpuid }, + {"cmpxchg8b", Op_cmpxchg8b}, + {"comisd", Op_SrcSrcSSEF}, + {"comiss", Op_SrcSrcSSEF}, + {"cpuid", Op_cpuid}, #ifdef ASM_X86_64 - { "cqo", Op_0_DXAX }, + {"cqo", Op_0_DXAX}, #endif - { "crc32", Op_DstSrc }, - { "cvtdq2pd", Op_DstSrcSSE }, - { "cvtdq2ps", Op_DstSrcSSE }, - { "cvtpd2dq", Op_DstSrcSSE }, - { "cvtpd2pi", Op_DstSrcSSE }, - { "cvtpd2ps", Op_DstSrcSSE }, - { "cvtpi2pd", Op_DstSrcSSE }, - { "cvtpi2ps", Op_DstSrcSSE }, - { "cvtps2dq", Op_DstSrcSSE }, - { "cvtps2pd", Op_DstSrcSSE }, - { "cvtps2pi", Op_DstSrcSSE }, - { "cvtsd2si", Op_DstSrcSSE }, - { "cvtsd2ss", Op_DstSrcSSE }, - { "cvtsi2sd", Op_DstSrcSSE }, - { "cvtsi2ss", Op_DstSrcSSE }, - { "cvtss2sd", Op_DstSrcSSE }, - { "cvtss2si", Op_DstSrcSSE }, - { "cvttpd2dq", Op_DstSrcSSE }, - { "cvttpd2pi", Op_DstSrcSSE }, - { "cvttps2dq", Op_DstSrcSSE }, - { "cvttps2pi", Op_DstSrcSSE }, - { "cvttsd2si", Op_DstSrcSSE }, - { "cvttss2si", Op_DstSrcSSE }, - { "cwd", Op_0_DXAX }, + {"crc32", Op_DstSrc}, + {"cvtdq2pd", Op_DstSrcSSE}, + {"cvtdq2ps", Op_DstSrcSSE}, + {"cvtpd2dq", Op_DstSrcSSE}, + {"cvtpd2pi", Op_DstSrcSSE}, + {"cvtpd2ps", Op_DstSrcSSE}, + {"cvtpi2pd", Op_DstSrcSSE}, + {"cvtpi2ps", Op_DstSrcSSE}, + {"cvtps2dq", Op_DstSrcSSE}, + {"cvtps2pd", Op_DstSrcSSE}, + {"cvtps2pi", Op_DstSrcSSE}, + {"cvtsd2si", Op_DstSrcSSE}, + {"cvtsd2ss", Op_DstSrcSSE}, + {"cvtsi2sd", Op_DstSrcSSE}, + {"cvtsi2ss", Op_DstSrcSSE}, + {"cvtss2sd", Op_DstSrcSSE}, + {"cvtss2si", Op_DstSrcSSE}, + {"cvttpd2dq", Op_DstSrcSSE}, + {"cvttpd2pi", Op_DstSrcSSE}, + {"cvttps2dq", Op_DstSrcSSE}, + {"cvttps2pi", Op_DstSrcSSE}, + {"cvttsd2si", Op_DstSrcSSE}, + {"cvttss2si", Op_DstSrcSSE}, + {"cwd", Op_0_DXAX}, #ifndef ASM_X86_64 - { "cwde", Op_0_AX }, + {"cwde", Op_0_AX}, #else - { "cwde", Op_0_DXAX }, + {"cwde", Op_0_DXAX}, #endif - //{ "da", Op_ }, // dunno what this is -- takes labels? +//{ "da", Op_ }, // dunno what this is -- takes labels? #ifndef ASM_X86_64 - { "daa", Op_Adjust }, - { "das", Op_Adjust }, + {"daa", Op_Adjust}, + {"das", Op_Adjust}, #endif - { "db", Op_db }, - { "dd", Op_dd }, - { "de", Op_de }, - { "dec", Op_UpdF }, - { "df", Op_df }, - { "di", Op_di }, - { "div", Op_Src_DXAXF }, - { "divpd", Op_DstSrcSSE }, - { "divps", Op_DstSrcSSE }, - { "divsd", Op_DstSrcSSE }, - { "divss", Op_DstSrcSSE }, - { "dl", Op_dl }, - { "dppd", Op_DstSrcImmS }, - { "dpps", Op_DstSrcImmS }, - { "dq", Op_dl }, - { "ds", Op_ds }, - { "dt", Op_de }, - { "dw", Op_ds }, - { "emms", Op_0 }, // clobber all mmx/fp? - { "enter", Op_enter }, - { "even", Op_Even }, - { "extractps", Op_ExtSrcImmS }, - { "f2xm1", Op_F0_ST }, // %% most of these are update... - { "fabs", Op_F0_ST }, - { "fadd", Op_FMath }, - { "faddp", Op_FPMath }, - { "fbld", Op_Fs_P }, - { "fbstp", Op_Fd_P }, - { "fchs", Op_F0_ST }, - { "fclex", Op_0 }, - { "fcmovb", Op_FdSTiSTi }, // but only ST(0) can be the destination -- should be FdST0STi - { "fcmovbe", Op_FdSTiSTi }, - { "fcmove", Op_FdSTiSTi }, - { "fcmovnb", Op_FdSTiSTi }, - { "fcmovnbe", Op_FdSTiSTi }, - { "fcmovne", Op_FdSTiSTi }, - { "fcmovnu", Op_FdSTiSTi }, - { "fcmovu", Op_FdSTiSTi }, - { "fcom", Op_FCmp }, - { "fcomi", Op_FCmpFlg }, - { "fcomip", Op_FCmpFlgP }, - { "fcomp", Op_FCmpP }, - { "fcompp", Op_F0_P }, // pops twice - { "fcos", Op_F0_ST }, - { "fdecstp", Op_F0_P }, // changes stack - { "fdisi", Op_fdisi }, - { "fdiv", Op_FMath }, - { "fdivp", Op_FPMath }, - { "fdivr", Op_FMath }, - { "fdivrp", Op_FPMath }, - { "feni", Op_feni }, - { "ffree", Op_FdST }, - { "fiadd", Op_Fis_ST }, - { "ficom", Op_Fis }, - { "ficomp", Op_Fis_P }, - { "fidiv", Op_Fis_ST }, - { "fidivr", Op_Fis_ST }, - { "fild", Op_Fis_P }, - { "fimul", Op_Fis_ST }, - { "fincstp", Op_F0_P }, - { "finit", Op_F0_P }, - { "fist", Op_Fid }, // only 16,32bit - { "fistp", Op_Fid_P }, - { "fisttp", Op_Fid_P }, - { "fisub", Op_Fis_ST }, - { "fisubr", Op_Fis_ST }, - { "fld", Op_fld }, - { "fld1", Op_F0_P }, - { "fldcw", Op_SrcMemNT }, - { "fldenv", Op_SrcMemNT }, - { "fldl2e", Op_F0_P }, - { "fldl2t", Op_F0_P }, - { "fldlg2", Op_F0_P }, - { "fldln2", Op_F0_P }, - { "fldpi", Op_F0_P }, - { "fldz", Op_F0_P }, - { "fmul", Op_FMath }, - { "fmulp", Op_FPMath }, - { "fnclex", Op_0 }, - { "fndisi", Op_fdisi }, // ?? - { "fneni", Op_feni }, // ?? - { "fninit", Op_0 }, - { "fnop", Op_0 }, - { "fnsave", Op_DstMemNT }, - { "fnstcw", Op_DstMemNT }, - { "fnstenv", Op_DstMemNT }, - { "fnstsw", Op_fXstsw }, - { "fpatan", Op_F0_P }, // pop and modify new ST - { "fprem", Op_F0_ST }, - { "fprem1", Op_F0_ST }, - { "fptan", Op_F0_P }, // modify ST and push 1.0 - { "frndint", Op_F0_ST }, - { "frstor", Op_SrcMemNT }, // but clobbers everything - { "fsave", Op_DstMemNT }, - { "fscale", Op_F0_ST }, - { "fsetpm", Op_fsetpm }, - { "fsin", Op_F0_ST }, - { "fsincos", Op_F0_P }, - { "fsqrt", Op_F0_ST }, - { "fst", Op_Ffd }, - { "fstcw", Op_DstMemNT }, - { "fstenv", Op_DstMemNT }, - { "fstp", Op_Ffd_P }, - { "fstsw", Op_fXstsw }, - { "fsub", Op_FMath }, - { "fsubp", Op_FPMath }, - { "fsubr", Op_FMath }, - { "fsubrp", Op_FPMath }, - { "ftst", Op_0 }, - { "fucom", Op_FCmp }, - { "fucomi", Op_FCmpFlg }, - { "fucomip", Op_FCmpFlgP }, - { "fucomp", Op_FCmpP }, - { "fucompp", Op_F0_P }, // pops twice - { "fwait", Op_0 }, - { "fxam", Op_0 }, - { "fxch", Op_fxch }, - { "fxrstor", Op_SrcMemNT }, // clobbers FP,MMX,SSE - { "fxsave", Op_DstMemNT }, - { "fxtract", Op_F0_P }, // pushes - { "fyl2x", Op_F0_P }, // pops - { "fyl2xp1", Op_F0_P }, // pops - { "haddpd", Op_DstSrcSSE }, - { "haddps", Op_DstSrcSSE }, - { "hlt", Op_0 }, - { "hsubpd", Op_DstSrcSSE }, - { "hsubps", Op_DstSrcSSE }, - { "idiv", Op_Src_DXAXF }, - { "imul", Op_imul }, - { "in", Op_in }, - { "inc", Op_UpdF }, - { "ins", Op_ins }, - { "insb", Op_insX }, - { "insd", Op_insX }, - { "insertps",Op_DstSrcImmS }, - { "insw", Op_insX }, - { "int", Op_SrcImm }, - { "into", Op_0 }, - { "invd", Op_0 }, - { "invlpg", Op_SrcMemNT }, - { "iret", Op_iret }, - { "iretd", Op_iretd }, + {"db", Op_db}, + {"dd", Op_dd}, + {"de", Op_de}, + {"dec", Op_UpdF}, + {"df", Op_df}, + {"di", Op_di}, + {"div", Op_Src_DXAXF}, + {"divpd", Op_DstSrcSSE}, + {"divps", Op_DstSrcSSE}, + {"divsd", Op_DstSrcSSE}, + {"divss", Op_DstSrcSSE}, + {"dl", Op_dl}, + {"dppd", Op_DstSrcImmS}, + {"dpps", Op_DstSrcImmS}, + {"dq", Op_dl}, + {"ds", Op_ds}, + {"dt", Op_de}, + {"dw", Op_ds}, + {"emms", Op_0}, // clobber all mmx/fp? + {"enter", Op_enter}, + {"even", Op_Even}, + {"extractps", Op_ExtSrcImmS}, + {"f2xm1", Op_F0_ST}, // %% most of these are update... + {"fabs", Op_F0_ST}, + {"fadd", Op_FMath}, + {"faddp", Op_FPMath}, + {"fbld", Op_Fs_P}, + {"fbstp", Op_Fd_P}, + {"fchs", Op_F0_ST}, + {"fclex", Op_0}, + {"fcmovb", Op_FdSTiSTi}, // but only ST(0) can be the destination -- should + // be FdST0STi + {"fcmovbe", Op_FdSTiSTi}, + {"fcmove", Op_FdSTiSTi}, + {"fcmovnb", Op_FdSTiSTi}, + {"fcmovnbe", Op_FdSTiSTi}, + {"fcmovne", Op_FdSTiSTi}, + {"fcmovnu", Op_FdSTiSTi}, + {"fcmovu", Op_FdSTiSTi}, + {"fcom", Op_FCmp}, + {"fcomi", Op_FCmpFlg}, + {"fcomip", Op_FCmpFlgP}, + {"fcomp", Op_FCmpP}, + {"fcompp", Op_F0_P}, // pops twice + {"fcos", Op_F0_ST}, + {"fdecstp", Op_F0_P}, // changes stack + {"fdisi", Op_fdisi}, + {"fdiv", Op_FMath}, + {"fdivp", Op_FPMath}, + {"fdivr", Op_FMath}, + {"fdivrp", Op_FPMath}, + {"feni", Op_feni}, + {"ffree", Op_FdST}, + {"fiadd", Op_Fis_ST}, + {"ficom", Op_Fis}, + {"ficomp", Op_Fis_P}, + {"fidiv", Op_Fis_ST}, + {"fidivr", Op_Fis_ST}, + {"fild", Op_Fis_P}, + {"fimul", Op_Fis_ST}, + {"fincstp", Op_F0_P}, + {"finit", Op_F0_P}, + {"fist", Op_Fid}, // only 16,32bit + {"fistp", Op_Fid_P}, + {"fisttp", Op_Fid_P}, + {"fisub", Op_Fis_ST}, + {"fisubr", Op_Fis_ST}, + {"fld", Op_fld}, + {"fld1", Op_F0_P}, + {"fldcw", Op_SrcMemNT}, + {"fldenv", Op_SrcMemNT}, + {"fldl2e", Op_F0_P}, + {"fldl2t", Op_F0_P}, + {"fldlg2", Op_F0_P}, + {"fldln2", Op_F0_P}, + {"fldpi", Op_F0_P}, + {"fldz", Op_F0_P}, + {"fmul", Op_FMath}, + {"fmulp", Op_FPMath}, + {"fnclex", Op_0}, + {"fndisi", Op_fdisi}, // ?? + {"fneni", Op_feni}, // ?? + {"fninit", Op_0}, + {"fnop", Op_0}, + {"fnsave", Op_DstMemNT}, + {"fnstcw", Op_DstMemNT}, + {"fnstenv", Op_DstMemNT}, + {"fnstsw", Op_fXstsw}, + {"fpatan", Op_F0_P}, // pop and modify new ST + {"fprem", Op_F0_ST}, + {"fprem1", Op_F0_ST}, + {"fptan", Op_F0_P}, // modify ST and push 1.0 + {"frndint", Op_F0_ST}, + {"frstor", Op_SrcMemNT}, // but clobbers everything + {"fsave", Op_DstMemNT}, + {"fscale", Op_F0_ST}, + {"fsetpm", Op_fsetpm}, + {"fsin", Op_F0_ST}, + {"fsincos", Op_F0_P}, + {"fsqrt", Op_F0_ST}, + {"fst", Op_Ffd}, + {"fstcw", Op_DstMemNT}, + {"fstenv", Op_DstMemNT}, + {"fstp", Op_Ffd_P}, + {"fstsw", Op_fXstsw}, + {"fsub", Op_FMath}, + {"fsubp", Op_FPMath}, + {"fsubr", Op_FMath}, + {"fsubrp", Op_FPMath}, + {"ftst", Op_0}, + {"fucom", Op_FCmp}, + {"fucomi", Op_FCmpFlg}, + {"fucomip", Op_FCmpFlgP}, + {"fucomp", Op_FCmpP}, + {"fucompp", Op_F0_P}, // pops twice + {"fwait", Op_0}, + {"fxam", Op_0}, + {"fxch", Op_fxch}, + {"fxrstor", Op_SrcMemNT}, // clobbers FP,MMX,SSE + {"fxsave", Op_DstMemNT}, + {"fxtract", Op_F0_P}, // pushes + {"fyl2x", Op_F0_P}, // pops + {"fyl2xp1", Op_F0_P}, // pops + {"haddpd", Op_DstSrcSSE}, + {"haddps", Op_DstSrcSSE}, + {"hlt", Op_0}, + {"hsubpd", Op_DstSrcSSE}, + {"hsubps", Op_DstSrcSSE}, + {"idiv", Op_Src_DXAXF}, + {"imul", Op_imul}, + {"in", Op_in}, + {"inc", Op_UpdF}, + {"ins", Op_ins}, + {"insb", Op_insX}, + {"insd", Op_insX}, + {"insertps", Op_DstSrcImmS}, + {"insw", Op_insX}, + {"int", Op_SrcImm}, + {"into", Op_0}, + {"invd", Op_0}, + {"invlpg", Op_SrcMemNT}, + {"iret", Op_iret}, + {"iretd", Op_iretd}, #ifdef ASM_X86_64 - { "iretq", Op_iretq }, // ? + {"iretq", Op_iretq}, // ? #endif - { "ja", Op_CBranch }, - { "jae", Op_CBranch }, - { "jb", Op_CBranch }, - { "jbe", Op_CBranch }, - { "jc", Op_CBranch }, - { "jcxz", Op_CBranch }, - { "je", Op_CBranch }, - { "jecxz", Op_CBranch }, - { "jg", Op_CBranch }, - { "jge", Op_CBranch }, - { "jl", Op_CBranch }, - { "jle", Op_CBranch }, - { "jmp", Op_Branch }, + {"ja", Op_CBranch}, + {"jae", Op_CBranch}, + {"jb", Op_CBranch}, + {"jbe", Op_CBranch}, + {"jc", Op_CBranch}, + {"jcxz", Op_CBranch}, + {"je", Op_CBranch}, + {"jecxz", Op_CBranch}, + {"jg", Op_CBranch}, + {"jge", Op_CBranch}, + {"jl", Op_CBranch}, + {"jle", Op_CBranch}, + {"jmp", Op_Branch}, #ifdef ASM_X86_64 - { "jmpe", Op_Branch }, // ? - { "jmpf", Op_Branch }, // ? + {"jmpe", Op_Branch}, // ? + {"jmpf", Op_Branch}, // ? #endif - { "jna", Op_CBranch }, - { "jnae", Op_CBranch }, - { "jnb", Op_CBranch }, - { "jnbe", Op_CBranch }, - { "jnc", Op_CBranch }, - { "jne", Op_CBranch }, - { "jng", Op_CBranch }, - { "jnge", Op_CBranch }, - { "jnl", Op_CBranch }, - { "jnle", Op_CBranch }, - { "jno", Op_CBranch }, - { "jnp", Op_CBranch }, - { "jns", Op_CBranch }, - { "jnz", Op_CBranch }, - { "jo", Op_CBranch }, - { "jp", Op_CBranch }, - { "jpe", Op_CBranch }, - { "jpo", Op_CBranch }, + {"jna", Op_CBranch}, + {"jnae", Op_CBranch}, + {"jnb", Op_CBranch}, + {"jnbe", Op_CBranch}, + {"jnc", Op_CBranch}, + {"jne", Op_CBranch}, + {"jng", Op_CBranch}, + {"jnge", Op_CBranch}, + {"jnl", Op_CBranch}, + {"jnle", Op_CBranch}, + {"jno", Op_CBranch}, + {"jnp", Op_CBranch}, + {"jns", Op_CBranch}, + {"jnz", Op_CBranch}, + {"jo", Op_CBranch}, + {"jp", Op_CBranch}, + {"jpe", Op_CBranch}, + {"jpo", Op_CBranch}, #ifdef ASM_X86_64 - { "jrcxz", Op_CBranch }, // Not supported by DMD + {"jrcxz", Op_CBranch}, // Not supported by DMD #endif - { "js", Op_CBranch }, - { "jz", Op_CBranch }, - { "lahf", Op_0_AX }, - { "lar", Op_DstSrcFW }, // reg dest only - { "lddqu", Op_DstSrcSSE }, - { "ldmxcsr", Op_SrcMemNT }, - { "lds", Op_DstSrc }, // reg dest only - { "lea", Op_DstSrc }, // " + {"js", Op_CBranch}, + {"jz", Op_CBranch}, + {"lahf", Op_0_AX}, + {"lar", Op_DstSrcFW}, // reg dest only + {"lddqu", Op_DstSrcSSE}, + {"ldmxcsr", Op_SrcMemNT}, + {"lds", Op_DstSrc}, // reg dest only + {"lea", Op_DstSrc}, // " #ifdef ASM_X86_64 - { "leaq", Op_DstSrcSSE }, // ? + {"leaq", Op_DstSrcSSE}, // ? #endif - { "leave", Op_0 }, // EBP,ESP clobbers + {"leave", Op_0}, // EBP,ESP clobbers #ifndef ASM_X86_64 - { "les", Op_DstSrc }, + {"les", Op_DstSrc}, #endif - { "lfence", Op_0 }, - { "lfs", Op_DstSrc }, - { "lgdt", Op_SrcMemNT }, - { "lgs", Op_DstSrc }, - { "lidt", Op_SrcMemNT }, - { "lldt", Op_SrcRMWNT }, - { "lmsw", Op_SrcRMWNT }, - { "lock", Op_0 }, - { "lods", Op_lods }, - { "lodsb", Op_lodsX }, - { "lodsd", Op_lodsX }, + {"lfence", Op_0}, + {"lfs", Op_DstSrc}, + {"lgdt", Op_SrcMemNT}, + {"lgs", Op_DstSrc}, + {"lidt", Op_SrcMemNT}, + {"lldt", Op_SrcRMWNT}, + {"lmsw", Op_SrcRMWNT}, + {"lock", Op_0}, + {"lods", Op_lods}, + {"lodsb", Op_lodsX}, + {"lodsd", Op_lodsX}, #ifdef ASM_X86_64 - { "lodsq", Op_lodsX }, + {"lodsq", Op_lodsX}, #endif - { "lodsw", Op_lodsX }, - { "loop", Op_Loop }, - { "loope", Op_Loop }, - { "loopne", Op_Loop }, - { "loopnz", Op_Loop }, - { "loopz", Op_Loop }, - { "lsl", Op_DstSrcFW }, // reg dest only - { "lss", Op_DstSrc }, - { "ltr", Op_DstMemNT }, - { "maskmovdqu", Op_SrcSrcMMX }, // writes to [edi] - { "maskmovq", Op_SrcSrcMMX }, - { "maxpd", Op_DstSrcSSE }, - { "maxps", Op_DstSrcSSE }, - { "maxsd", Op_DstSrcSSE }, - { "maxss", Op_DstSrcSSE }, - { "mfence", Op_0}, - { "minpd", Op_DstSrcSSE }, - { "minps", Op_DstSrcSSE }, - { "minsd", Op_DstSrcSSE }, - { "minss", Op_DstSrcSSE }, - { "monitor", Op_0 }, - { "mov", Op_DstSrc }, - { "movapd", Op_DstSrcSSE }, - { "movaps", Op_DstSrcSSE }, + {"lodsw", Op_lodsX}, + {"loop", Op_Loop}, + {"loope", Op_Loop}, + {"loopne", Op_Loop}, + {"loopnz", Op_Loop}, + {"loopz", Op_Loop}, + {"lsl", Op_DstSrcFW}, // reg dest only + {"lss", Op_DstSrc}, + {"ltr", Op_DstMemNT}, + {"maskmovdqu", Op_SrcSrcMMX}, // writes to [edi] + {"maskmovq", Op_SrcSrcMMX}, + {"maxpd", Op_DstSrcSSE}, + {"maxps", Op_DstSrcSSE}, + {"maxsd", Op_DstSrcSSE}, + {"maxss", Op_DstSrcSSE}, + {"mfence", Op_0}, + {"minpd", Op_DstSrcSSE}, + {"minps", Op_DstSrcSSE}, + {"minsd", Op_DstSrcSSE}, + {"minss", Op_DstSrcSSE}, + {"monitor", Op_0}, + {"mov", Op_DstSrc}, + {"movapd", Op_DstSrcSSE}, + {"movaps", Op_DstSrcSSE}, #ifdef ASM_X86_64 - { "movb", Op_DstSrcNT }, // ? + {"movb", Op_DstSrcNT}, // ? #endif - { "movd", Op_DstSrcNT }, // also mmx and sse - { "movddup", Op_DstSrcSSE }, - { "movdq2q", Op_DstSrcNT }, // mmx/sse - { "movdqa", Op_DstSrcSSE }, - { "movdqu", Op_DstSrcSSE }, - { "movhlps", Op_DstSrcSSE }, - { "movhpd", Op_DstSrcSSE }, - { "movhps", Op_DstSrcSSE }, + {"movd", Op_DstSrcNT}, // also mmx and sse + {"movddup", Op_DstSrcSSE}, + {"movdq2q", Op_DstSrcNT}, // mmx/sse + {"movdqa", Op_DstSrcSSE}, + {"movdqu", Op_DstSrcSSE}, + {"movhlps", Op_DstSrcSSE}, + {"movhpd", Op_DstSrcSSE}, + {"movhps", Op_DstSrcSSE}, #ifdef ASM_X86_64 - { "movl", Op_DstSrc }, // ? + {"movl", Op_DstSrc}, // ? #endif - { "movlhps", Op_DstSrcSSE }, - { "movlpd", Op_DstSrcSSE }, - { "movlps", Op_DstSrcSSE }, - { "movmskpd", Op_DstSrcSSE }, - { "movmskps", Op_DstSrcSSE }, - { "movntdq", Op_DstSrcNT }, // limited to sse, but mem dest - { "movntdqa",Op_DstSrcNT }, - { "movnti", Op_DstSrcNT }, // limited to gpr, but mem dest - { "movntpd", Op_DstSrcNT }, // limited to sse, but mem dest - { "movntps", Op_DstSrcNT }, // limited to sse, but mem dest - { "movntq", Op_DstSrcNT }, // limited to mmx, but mem dest - { "movq", Op_DstSrcNT }, // limited to sse and mmx - { "movq2dq", Op_DstSrcNT }, // limited to sse <- mmx regs - { "movs", Op_movs }, - { "movsb", Op_movsX }, - { "movsd", Op_movsd }, - { "movshdup", Op_DstSrcSSE }, - { "movsldup", Op_DstSrcSSE }, + {"movlhps", Op_DstSrcSSE}, + {"movlpd", Op_DstSrcSSE}, + {"movlps", Op_DstSrcSSE}, + {"movmskpd", Op_DstSrcSSE}, + {"movmskps", Op_DstSrcSSE}, + {"movntdq", Op_DstSrcNT}, // limited to sse, but mem dest + {"movntdqa", Op_DstSrcNT}, + {"movnti", Op_DstSrcNT}, // limited to gpr, but mem dest + {"movntpd", Op_DstSrcNT}, // limited to sse, but mem dest + {"movntps", Op_DstSrcNT}, // limited to sse, but mem dest + {"movntq", Op_DstSrcNT}, // limited to mmx, but mem dest + {"movq", Op_DstSrcNT}, // limited to sse and mmx + {"movq2dq", Op_DstSrcNT}, // limited to sse <- mmx regs + {"movs", Op_movs}, + {"movsb", Op_movsX}, + {"movsd", Op_movsd}, + {"movshdup", Op_DstSrcSSE}, + {"movsldup", Op_DstSrcSSE}, #ifdef ASM_X86_64 - { "movsq", Op_movsd }, + {"movsq", Op_movsd}, #endif - { "movss", Op_DstSrcSSE }, - { "movsw", Op_movsX }, - { "movsx", Op_movsx }, // word-only, reg dest + {"movss", Op_DstSrcSSE}, + {"movsw", Op_movsX}, + {"movsx", Op_movsx}, // word-only, reg dest #ifdef ASM_X86_64 - { "movsxd", Op_movsx }, + {"movsxd", Op_movsx}, #endif - { "movupd", Op_DstSrcSSE }, - { "movups", Op_DstSrcSSE }, + {"movupd", Op_DstSrcSSE}, + {"movups", Op_DstSrcSSE}, #ifdef ASM_X86_64 - { "movzbl", Op_DstSrcNT }, + {"movzbl", Op_DstSrcNT}, #endif - { "movzx", Op_movzx }, - { "mpsadbw", Op_DstSrcImmS }, - { "mul", Op_mul }, - { "mulpd", Op_DstSrcSSE }, - { "mulps", Op_DstSrcSSE }, - { "mulsd", Op_DstSrcSSE }, - { "mulss", Op_DstSrcSSE }, - { "mwait", Op_0 }, - { "naked", Op_Naked }, - { "neg", Op_UpdF }, - { "nop", Op_0 }, - { "not", Op_Upd }, - { "or", Op_UpdSrcF }, - { "orpd", Op_DstSrcSSE }, - { "orps", Op_DstSrcSSE }, - { "out", Op_out }, - { "outs", Op_outs }, - { "outsb", Op_outsX }, - { "outsd", Op_outsX }, - { "outsw", Op_outsX }, + {"movzx", Op_movzx}, + {"mpsadbw", Op_DstSrcImmS}, + {"mul", Op_mul}, + {"mulpd", Op_DstSrcSSE}, + {"mulps", Op_DstSrcSSE}, + {"mulsd", Op_DstSrcSSE}, + {"mulss", Op_DstSrcSSE}, + {"mwait", Op_0}, + {"naked", Op_Naked}, + {"neg", Op_UpdF}, + {"nop", Op_0}, + {"not", Op_Upd}, + {"or", Op_UpdSrcF}, + {"orpd", Op_DstSrcSSE}, + {"orps", Op_DstSrcSSE}, + {"out", Op_out}, + {"outs", Op_outs}, + {"outsb", Op_outsX}, + {"outsd", Op_outsX}, + {"outsw", Op_outsX}, #ifdef ASM_X86_64 - { "pabsb", Op_DstSrcSSE }, - { "pabsq", Op_DstSrcSSE }, - { "pabsw", Op_DstSrcSSE }, + {"pabsb", Op_DstSrcSSE}, + {"pabsq", Op_DstSrcSSE}, + {"pabsw", Op_DstSrcSSE}, #endif - { "packssdw", Op_DstSrcMMX }, // %% also SSE - { "packsswb", Op_DstSrcMMX }, - { "packusdw", Op_DstSrcSSE }, - { "packuswb", Op_DstSrcMMX }, - { "paddb", Op_DstSrcMMX }, - { "paddd", Op_DstSrcMMX }, - { "paddq", Op_DstSrcMMX }, - { "paddsb", Op_DstSrcMMX }, - { "paddsw", Op_DstSrcMMX }, - { "paddusb", Op_DstSrcMMX }, - { "paddusw", Op_DstSrcMMX }, - { "paddw", Op_DstSrcMMX }, - { "palignr", Op_DstSrcImmS }, - { "pand", Op_DstSrcMMX }, - { "pandn", Op_DstSrcMMX }, + {"packssdw", Op_DstSrcMMX}, // %% also SSE + {"packsswb", Op_DstSrcMMX}, + {"packusdw", Op_DstSrcSSE}, + {"packuswb", Op_DstSrcMMX}, + {"paddb", Op_DstSrcMMX}, + {"paddd", Op_DstSrcMMX}, + {"paddq", Op_DstSrcMMX}, + {"paddsb", Op_DstSrcMMX}, + {"paddsw", Op_DstSrcMMX}, + {"paddusb", Op_DstSrcMMX}, + {"paddusw", Op_DstSrcMMX}, + {"paddw", Op_DstSrcMMX}, + {"palignr", Op_DstSrcImmS}, + {"pand", Op_DstSrcMMX}, + {"pandn", Op_DstSrcMMX}, #ifdef ASM_X86_64 - { "pause", Op_DstSrcMMX }, // ? + {"pause", Op_DstSrcMMX}, // ? #endif - { "pavgb", Op_DstSrcMMX }, - { "pavgusb", Op_DstSrcMMX }, // AMD 3dNow! - { "pavgw", Op_DstSrcMMX }, - { "pblendvb", Op_DstSrcSSE }, // implicit xmm0 - { "pblendw", Op_DstSrcImmS }, - { "pcmpeqb", Op_DstSrcMMX }, - { "pcmpeqd", Op_DstSrcMMX }, - { "pcmpeqq", Op_DstSrcSSE }, - { "pcmpeqw", Op_DstSrcMMX }, - { "pcmpestri", Op_DstSrcImmS }, - { "pcmpestrm", Op_DstSrcImmS }, - { "pcmpgtb", Op_DstSrcMMX }, - { "pcmpgtd", Op_DstSrcMMX }, - { "pcmpgtq", Op_DstSrcSSE }, - { "pcmpgtw", Op_DstSrcMMX }, - { "pcmpistri", Op_DstSrcImmS }, - { "pcmpistrm", Op_DstSrcImmS }, - { "pextrb", Op_DstSrcImmM }, // gpr32 dest - { "pextrd", Op_DstSrcImmM }, // gpr32 dest - { "pextrq", Op_DstSrcImmM }, // gpr32 dest - { "pextrw", Op_DstSrcImmM }, // gpr32 dest - { "pf2id", Op_DstSrcMMX }, // %% AMD 3dNow! opcodes - { "pfacc", Op_DstSrcMMX }, - { "pfadd", Op_DstSrcMMX }, - { "pfcmpeq", Op_DstSrcMMX }, - { "pfcmpge", Op_DstSrcMMX }, - { "pfcmpgt", Op_DstSrcMMX }, - { "pfmax", Op_DstSrcMMX }, - { "pfmin", Op_DstSrcMMX }, - { "pfmul", Op_DstSrcMMX }, - { "pfnacc", Op_DstSrcMMX }, // 3dNow values are returned in MM0 register, - { "pfpnacc", Op_DstSrcMMX }, // so should be correct to use Op_DstSrcMMX. - { "pfrcp", Op_DstSrcMMX }, - { "pfrcpit1", Op_DstSrcMMX }, - { "pfrcpit2", Op_DstSrcMMX }, - { "pfrsqit1", Op_DstSrcMMX }, - { "pfrsqrt", Op_DstSrcMMX }, - { "pfsub", Op_DstSrcMMX }, - { "pfsubr", Op_DstSrcMMX }, + {"pavgb", Op_DstSrcMMX}, + {"pavgusb", Op_DstSrcMMX}, // AMD 3dNow! + {"pavgw", Op_DstSrcMMX}, + {"pblendvb", Op_DstSrcSSE}, // implicit xmm0 + {"pblendw", Op_DstSrcImmS}, + {"pcmpeqb", Op_DstSrcMMX}, + {"pcmpeqd", Op_DstSrcMMX}, + {"pcmpeqq", Op_DstSrcSSE}, + {"pcmpeqw", Op_DstSrcMMX}, + {"pcmpestri", Op_DstSrcImmS}, + {"pcmpestrm", Op_DstSrcImmS}, + {"pcmpgtb", Op_DstSrcMMX}, + {"pcmpgtd", Op_DstSrcMMX}, + {"pcmpgtq", Op_DstSrcSSE}, + {"pcmpgtw", Op_DstSrcMMX}, + {"pcmpistri", Op_DstSrcImmS}, + {"pcmpistrm", Op_DstSrcImmS}, + {"pextrb", Op_DstSrcImmM}, // gpr32 dest + {"pextrd", Op_DstSrcImmM}, // gpr32 dest + {"pextrq", Op_DstSrcImmM}, // gpr32 dest + {"pextrw", Op_DstSrcImmM}, // gpr32 dest + {"pf2id", Op_DstSrcMMX}, // %% AMD 3dNow! opcodes + {"pfacc", Op_DstSrcMMX}, + {"pfadd", Op_DstSrcMMX}, + {"pfcmpeq", Op_DstSrcMMX}, + {"pfcmpge", Op_DstSrcMMX}, + {"pfcmpgt", Op_DstSrcMMX}, + {"pfmax", Op_DstSrcMMX}, + {"pfmin", Op_DstSrcMMX}, + {"pfmul", Op_DstSrcMMX}, + {"pfnacc", Op_DstSrcMMX}, // 3dNow values are returned in MM0 register, + {"pfpnacc", Op_DstSrcMMX}, // so should be correct to use Op_DstSrcMMX. + {"pfrcp", Op_DstSrcMMX}, + {"pfrcpit1", Op_DstSrcMMX}, + {"pfrcpit2", Op_DstSrcMMX}, + {"pfrsqit1", Op_DstSrcMMX}, + {"pfrsqrt", Op_DstSrcMMX}, + {"pfsub", Op_DstSrcMMX}, + {"pfsubr", Op_DstSrcMMX}, #ifdef ASM_X86_64 - { "phaddd", Op_DstSrcSSE }, - { "phaddsw", Op_DstSrcSSE }, - { "phaddw", Op_DstSrcSSE }, - { "phminposuw", Op_DstSrcSSE }, - { "phsubd", Op_DstSrcSSE }, - { "phsubsw", Op_DstSrcSSE }, - { "phsubw", Op_DstSrcSSE }, + {"phaddd", Op_DstSrcSSE}, + {"phaddsw", Op_DstSrcSSE}, + {"phaddw", Op_DstSrcSSE}, + {"phminposuw", Op_DstSrcSSE}, + {"phsubd", Op_DstSrcSSE}, + {"phsubsw", Op_DstSrcSSE}, + {"phsubw", Op_DstSrcSSE}, #endif - { "pi2fd", Op_DstSrcMMX }, // %% - { "pinsrb", Op_DstSrcImmM }, // gpr32(16), mem16 src, sse too - { "pinsrd", Op_DstSrcImmM }, // gpr32(16), mem16 src, sse too - { "pinsrq", Op_DstSrcImmM }, // gpr32(16), mem16 src, sse too - { "pinsrw", Op_DstSrcImmM }, // gpr32(16), mem16 src, sse too + {"pi2fd", Op_DstSrcMMX}, // %% + {"pinsrb", Op_DstSrcImmM}, // gpr32(16), mem16 src, sse too + {"pinsrd", Op_DstSrcImmM}, // gpr32(16), mem16 src, sse too + {"pinsrq", Op_DstSrcImmM}, // gpr32(16), mem16 src, sse too + {"pinsrw", Op_DstSrcImmM}, // gpr32(16), mem16 src, sse too #ifdef ASM_X86_64 - { "pmaddubsw", Op_DstSrcSSE }, + {"pmaddubsw", Op_DstSrcSSE}, #endif - { "pmaddwd", Op_DstSrcMMX }, - { "pmaxsb", Op_DstSrcSSE }, - { "pmaxsd", Op_DstSrcSSE }, - { "pmaxsw", Op_DstSrcMMX }, - { "pmaxub", Op_DstSrcMMX }, - { "pmaxud", Op_DstSrcSSE }, - { "pmaxuw", Op_DstSrcSSE }, - { "pminsb", Op_DstSrcSSE }, - { "pminsd", Op_DstSrcSSE }, - { "pminsw", Op_DstSrcMMX }, - { "pminub", Op_DstSrcMMX }, - { "pminud", Op_DstSrcSSE }, - { "pminuw", Op_DstSrcSSE }, - { "pmovmskb", Op_DstSrcMMX }, - { "pmovsxbd", Op_DstSrcSSE }, - { "pmovsxbq", Op_DstSrcSSE }, - { "pmovsxbw", Op_DstSrcSSE }, - { "pmovsxdq", Op_DstSrcSSE }, - { "pmovsxwd", Op_DstSrcSSE }, - { "pmovsxwq", Op_DstSrcSSE }, - { "pmovzxbd", Op_DstSrcSSE }, - { "pmovzxbq", Op_DstSrcSSE }, - { "pmovzxbw", Op_DstSrcSSE }, - { "pmovzxdq", Op_DstSrcSSE }, - { "pmovzxwd", Op_DstSrcSSE }, - { "pmovzxwq", Op_DstSrcSSE }, - { "pmuldq", Op_DstSrcSSE }, + {"pmaddwd", Op_DstSrcMMX}, + {"pmaxsb", Op_DstSrcSSE}, + {"pmaxsd", Op_DstSrcSSE}, + {"pmaxsw", Op_DstSrcMMX}, + {"pmaxub", Op_DstSrcMMX}, + {"pmaxud", Op_DstSrcSSE}, + {"pmaxuw", Op_DstSrcSSE}, + {"pminsb", Op_DstSrcSSE}, + {"pminsd", Op_DstSrcSSE}, + {"pminsw", Op_DstSrcMMX}, + {"pminub", Op_DstSrcMMX}, + {"pminud", Op_DstSrcSSE}, + {"pminuw", Op_DstSrcSSE}, + {"pmovmskb", Op_DstSrcMMX}, + {"pmovsxbd", Op_DstSrcSSE}, + {"pmovsxbq", Op_DstSrcSSE}, + {"pmovsxbw", Op_DstSrcSSE}, + {"pmovsxdq", Op_DstSrcSSE}, + {"pmovsxwd", Op_DstSrcSSE}, + {"pmovsxwq", Op_DstSrcSSE}, + {"pmovzxbd", Op_DstSrcSSE}, + {"pmovzxbq", Op_DstSrcSSE}, + {"pmovzxbw", Op_DstSrcSSE}, + {"pmovzxdq", Op_DstSrcSSE}, + {"pmovzxwd", Op_DstSrcSSE}, + {"pmovzxwq", Op_DstSrcSSE}, + {"pmuldq", Op_DstSrcSSE}, #ifdef ASM_X86_64 - { "pmulhrsw", Op_DstSrcMMX }, + {"pmulhrsw", Op_DstSrcMMX}, #endif - { "pmulhrw", Op_DstSrcMMX }, // AMD 3dNow! - { "pmulhuw", Op_DstSrcMMX }, - { "pmulhw", Op_DstSrcMMX }, - { "pmulld", Op_DstSrcSSE }, - { "pmullw", Op_DstSrcMMX }, - { "pmuludq", Op_DstSrcMMX }, // also sse - { "pop", Op_DstW }, + {"pmulhrw", Op_DstSrcMMX}, // AMD 3dNow! + {"pmulhuw", Op_DstSrcMMX}, + {"pmulhw", Op_DstSrcMMX}, + {"pmulld", Op_DstSrcSSE}, + {"pmullw", Op_DstSrcMMX}, + {"pmuludq", Op_DstSrcMMX}, // also sse + {"pop", Op_DstW}, #ifndef ASM_X86_64 - { "popa", Op_SizedStack }, // For intel this is always 16-bit - { "popad", Op_SizedStack }, // GAS doesn't accept 'popad' -- these clobber everything, but supposedly it would be used to preserve clobbered regs + {"popa", Op_SizedStack}, // For intel this is always 16-bit + {"popad", Op_SizedStack}, // GAS doesn't accept 'popad' -- these clobber + // everything, but supposedly it would be used to + // preserve clobbered regs #endif - { "popcnt", Op_DstSrc }, - { "popf", Op_0 }, // rewrite the insn with a special case + {"popcnt", Op_DstSrc}, + {"popf", Op_0}, // rewrite the insn with a special case #ifndef ASM_X86_64 - { "popfd", Op_0 }, + {"popfd", Op_0}, #else - { "popfq", Op_0 }, // Op_SizedStack? - { "popq", Op_push }, + {"popfq", Op_0}, // Op_SizedStack? + {"popq", Op_push}, #endif - { "por", Op_DstSrcMMX }, - { "prefetchnta", Op_SrcMemNT }, - { "prefetcht0", Op_SrcMemNT }, - { "prefetcht1", Op_SrcMemNT }, - { "prefetcht2", Op_SrcMemNT }, - { "psadbw", Op_DstSrcMMX }, - { "pshufb", Op_DstSrcSSE }, + {"por", Op_DstSrcMMX}, + {"prefetchnta", Op_SrcMemNT}, + {"prefetcht0", Op_SrcMemNT}, + {"prefetcht1", Op_SrcMemNT}, + {"prefetcht2", Op_SrcMemNT}, + {"psadbw", Op_DstSrcMMX}, + {"pshufb", Op_DstSrcSSE}, #ifndef ASM_X86_64 - { "pshufd", Op_DstSrcImmM }, - { "pshufhw", Op_DstSrcImmM }, - { "pshuflw", Op_DstSrcImmM }, - { "pshufw", Op_DstSrcImmM }, + {"pshufd", Op_DstSrcImmM}, + {"pshufhw", Op_DstSrcImmM}, + {"pshuflw", Op_DstSrcImmM}, + {"pshufw", Op_DstSrcImmM}, #else - { "pshufd", Op_DstSrcImmS }, - { "pshufhw", Op_DstSrcImmS }, - { "pshuflw", Op_DstSrcImmS }, - { "pshufw", Op_DstSrcImmS }, - { "psignb", Op_DstSrcSSE }, - { "psignd", Op_DstSrcSSE }, - { "psignw", Op_DstSrcSSE }, + {"pshufd", Op_DstSrcImmS}, + {"pshufhw", Op_DstSrcImmS}, + {"pshuflw", Op_DstSrcImmS}, + {"pshufw", Op_DstSrcImmS}, + {"psignb", Op_DstSrcSSE}, + {"psignd", Op_DstSrcSSE}, + {"psignw", Op_DstSrcSSE}, #endif - { "pslld", Op_DstSrcMMX }, // immediate operands... - { "pslldq", Op_DstSrcMMX }, - { "psllq", Op_DstSrcMMX }, - { "psllw", Op_DstSrcMMX }, - { "psrad", Op_DstSrcMMX }, - { "psraw", Op_DstSrcMMX }, - { "psrld", Op_DstSrcMMX }, - { "psrldq", Op_DstSrcMMX }, - { "psrlq", Op_DstSrcMMX }, - { "psrlw", Op_DstSrcMMX }, - { "psubb", Op_DstSrcMMX }, - { "psubd", Op_DstSrcMMX }, - { "psubq", Op_DstSrcMMX }, - { "psubsb", Op_DstSrcMMX }, - { "psubsw", Op_DstSrcMMX }, - { "psubusb", Op_DstSrcMMX }, - { "psubusw", Op_DstSrcMMX }, - { "psubw", Op_DstSrcMMX }, - { "pswapd", Op_DstSrcMMX }, // AMD 3dNow! - { "ptest", Op_SrcSrcSSEF }, - { "punpckhbw", Op_DstSrcMMX }, - { "punpckhdq", Op_DstSrcMMX }, - { "punpckhqdq", Op_DstSrcMMX }, - { "punpckhwd", Op_DstSrcMMX }, - { "punpcklbw", Op_DstSrcMMX }, - { "punpckldq", Op_DstSrcMMX }, - { "punpcklqdq", Op_DstSrcMMX }, - { "punpcklwd", Op_DstSrcMMX }, - { "push", Op_push }, + {"pslld", Op_DstSrcMMX}, // immediate operands... + {"pslldq", Op_DstSrcMMX}, + {"psllq", Op_DstSrcMMX}, + {"psllw", Op_DstSrcMMX}, + {"psrad", Op_DstSrcMMX}, + {"psraw", Op_DstSrcMMX}, + {"psrld", Op_DstSrcMMX}, + {"psrldq", Op_DstSrcMMX}, + {"psrlq", Op_DstSrcMMX}, + {"psrlw", Op_DstSrcMMX}, + {"psubb", Op_DstSrcMMX}, + {"psubd", Op_DstSrcMMX}, + {"psubq", Op_DstSrcMMX}, + {"psubsb", Op_DstSrcMMX}, + {"psubsw", Op_DstSrcMMX}, + {"psubusb", Op_DstSrcMMX}, + {"psubusw", Op_DstSrcMMX}, + {"psubw", Op_DstSrcMMX}, + {"pswapd", Op_DstSrcMMX}, // AMD 3dNow! + {"ptest", Op_SrcSrcSSEF}, + {"punpckhbw", Op_DstSrcMMX}, + {"punpckhdq", Op_DstSrcMMX}, + {"punpckhqdq", Op_DstSrcMMX}, + {"punpckhwd", Op_DstSrcMMX}, + {"punpcklbw", Op_DstSrcMMX}, + {"punpckldq", Op_DstSrcMMX}, + {"punpcklqdq", Op_DstSrcMMX}, + {"punpcklwd", Op_DstSrcMMX}, + {"push", Op_push}, #ifndef ASM_X86_64 - { "pusha", Op_SizedStack }, - { "pushad", Op_SizedStack }, + {"pusha", Op_SizedStack}, + {"pushad", Op_SizedStack}, #endif - { "pushf", Op_0 }, + {"pushf", Op_0}, #ifndef ASM_X86_64 - { "pushfd", Op_0 }, + {"pushfd", Op_0}, #else - { "pushfq", Op_0 }, // Op_SizedStack? - { "pushq", Op_push }, // ? + {"pushfq", Op_0}, // Op_SizedStack? + {"pushq", Op_push}, // ? #endif - { "pxor", Op_DstSrcMMX }, - { "rcl", Op_Shift }, // limited src operands -- change to shift - { "rcpps", Op_DstSrcSSE }, - { "rcpss", Op_DstSrcSSE }, - { "rcr", Op_Shift }, -// { rdfsbase, XXXX }, -// { rdgsbase, XXXX }, - { "rdmsr", Op_0_DXAX }, - { "rdpmc", Op_0_DXAX }, -// { rdrand, XXXX }, - { "rdtsc", Op_0_DXAX }, - { "rep", Op_0 }, - { "repe", Op_0 }, - { "repne", Op_0 }, - { "repnz", Op_0 }, - { "repz", Op_0 }, - { "ret", Op_ret }, - { "retf", Op_retf }, + {"pxor", Op_DstSrcMMX}, + {"rcl", Op_Shift}, // limited src operands -- change to shift + {"rcpps", Op_DstSrcSSE}, + {"rcpss", Op_DstSrcSSE}, + {"rcr", Op_Shift}, + // { rdfsbase, XXXX }, + // { rdgsbase, XXXX }, + {"rdmsr", Op_0_DXAX}, + {"rdpmc", Op_0_DXAX}, + // { rdrand, XXXX }, + {"rdtsc", Op_0_DXAX}, + {"rep", Op_0}, + {"repe", Op_0}, + {"repne", Op_0}, + {"repnz", Op_0}, + {"repz", Op_0}, + {"ret", Op_ret}, + {"retf", Op_retf}, #ifdef ASM_X86_64 - { "retn", Op_retf }, // ? + {"retn", Op_retf}, // ? #endif - { "rol", Op_Shift }, - { "ror", Op_Shift }, - { "roundpd", Op_DstSrcImmS }, - { "roundps", Op_DstSrcImmS }, - { "roundsd", Op_DstSrcImmS }, - { "roundss", Op_DstSrcImmS }, - { "rsm", Op_0 }, - { "rsqrtps", Op_DstSrcSSE }, - { "rsqrtss", Op_DstSrcSSE }, - { "sahf", Op_Flags }, - { "sal", Op_Shift }, + {"rol", Op_Shift}, + {"ror", Op_Shift}, + {"roundpd", Op_DstSrcImmS}, + {"roundps", Op_DstSrcImmS}, + {"roundsd", Op_DstSrcImmS}, + {"roundss", Op_DstSrcImmS}, + {"rsm", Op_0}, + {"rsqrtps", Op_DstSrcSSE}, + {"rsqrtss", Op_DstSrcSSE}, + {"sahf", Op_Flags}, + {"sal", Op_Shift}, #ifdef ASM_X86_64 - { "salq", Op_DstSrcNT }, + {"salq", Op_DstSrcNT}, #endif - { "sar", Op_Shift }, - { "sbb", Op_UpdSrcF }, - { "scas", Op_scas }, - { "scasb", Op_scasX }, - { "scasd", Op_scasX }, + {"sar", Op_Shift}, + {"sbb", Op_UpdSrcF}, + {"scas", Op_scas}, + {"scasb", Op_scasX}, + {"scasd", Op_scasX}, #ifdef ASM_X86_64 - { "scasq", Op_scasX }, + {"scasq", Op_scasX}, #endif - { "scasw", Op_scasX }, - { "seta", Op_DstRMBNT }, // also gpr8 - { "setae", Op_DstRMBNT }, - { "setb", Op_DstRMBNT }, - { "setbe", Op_DstRMBNT }, - { "setc", Op_DstRMBNT }, - { "sete", Op_DstRMBNT }, - { "setg", Op_DstRMBNT }, - { "setge", Op_DstRMBNT }, - { "setl", Op_DstRMBNT }, - { "setle", Op_DstRMBNT }, - { "setna", Op_DstRMBNT }, - { "setnae", Op_DstRMBNT }, - { "setnb", Op_DstRMBNT }, - { "setnbe", Op_DstRMBNT }, - { "setnc", Op_DstRMBNT }, - { "setne", Op_DstRMBNT }, - { "setng", Op_DstRMBNT }, - { "setnge", Op_DstRMBNT }, - { "setnl", Op_DstRMBNT }, - { "setnle", Op_DstRMBNT }, - { "setno", Op_DstRMBNT }, - { "setnp", Op_DstRMBNT }, - { "setns", Op_DstRMBNT }, - { "setnz", Op_DstRMBNT }, - { "seto", Op_DstRMBNT }, - { "setp", Op_DstRMBNT }, - { "setpe", Op_DstRMBNT }, - { "setpo", Op_DstRMBNT }, - { "sets", Op_DstRMBNT }, - { "setz", Op_DstRMBNT }, - { "sfence", Op_0 }, - { "sgdt", Op_DstMemNT }, - { "sha1msg1", Op_DstSrcSSE }, - { "sha1msg2", Op_DstSrcSSE }, - { "sha1nexte", Op_DstSrcSSE }, - { "sha1rnds4", Op_DstSrcImmS }, - { "sha256msg1", Op_DstSrcSSE }, - { "sha256msg2", Op_DstSrcSSE }, - { "sha256rnds2", Op_DstSrcSSE }, // implicit xmm0 - { "shl", Op_Shift }, - { "shld", Op_UpdSrcShft }, - { "shr", Op_Shift }, - { "shrd", Op_UpdSrcShft }, - { "shufpd", Op_DstSrcImmS }, - { "shufps", Op_DstSrcImmS }, - { "sidt", Op_DstMemNT }, - { "sldt", Op_DstRMWNT }, - { "smsw", Op_DstRMWNT }, - { "sqrtpd", Op_DstSrcSSE }, - { "sqrtps", Op_DstSrcSSE }, - { "sqrtsd", Op_DstSrcSSE }, - { "sqrtss", Op_DstSrcSSE }, - { "stc", Op_Flags }, - { "std", Op_Flags }, - { "sti", Op_Flags }, - { "stmxcsr", Op_DstMemNT }, - { "stos", Op_stos }, - { "stosb", Op_stosX }, - { "stosd", Op_stosX }, + {"scasw", Op_scasX}, + {"seta", Op_DstRMBNT}, // also gpr8 + {"setae", Op_DstRMBNT}, + {"setb", Op_DstRMBNT}, + {"setbe", Op_DstRMBNT}, + {"setc", Op_DstRMBNT}, + {"sete", Op_DstRMBNT}, + {"setg", Op_DstRMBNT}, + {"setge", Op_DstRMBNT}, + {"setl", Op_DstRMBNT}, + {"setle", Op_DstRMBNT}, + {"setna", Op_DstRMBNT}, + {"setnae", Op_DstRMBNT}, + {"setnb", Op_DstRMBNT}, + {"setnbe", Op_DstRMBNT}, + {"setnc", Op_DstRMBNT}, + {"setne", Op_DstRMBNT}, + {"setng", Op_DstRMBNT}, + {"setnge", Op_DstRMBNT}, + {"setnl", Op_DstRMBNT}, + {"setnle", Op_DstRMBNT}, + {"setno", Op_DstRMBNT}, + {"setnp", Op_DstRMBNT}, + {"setns", Op_DstRMBNT}, + {"setnz", Op_DstRMBNT}, + {"seto", Op_DstRMBNT}, + {"setp", Op_DstRMBNT}, + {"setpe", Op_DstRMBNT}, + {"setpo", Op_DstRMBNT}, + {"sets", Op_DstRMBNT}, + {"setz", Op_DstRMBNT}, + {"sfence", Op_0}, + {"sgdt", Op_DstMemNT}, + {"sha1msg1", Op_DstSrcSSE}, + {"sha1msg2", Op_DstSrcSSE}, + {"sha1nexte", Op_DstSrcSSE}, + {"sha1rnds4", Op_DstSrcImmS}, + {"sha256msg1", Op_DstSrcSSE}, + {"sha256msg2", Op_DstSrcSSE}, + {"sha256rnds2", Op_DstSrcSSE}, // implicit xmm0 + {"shl", Op_Shift}, + {"shld", Op_UpdSrcShft}, + {"shr", Op_Shift}, + {"shrd", Op_UpdSrcShft}, + {"shufpd", Op_DstSrcImmS}, + {"shufps", Op_DstSrcImmS}, + {"sidt", Op_DstMemNT}, + {"sldt", Op_DstRMWNT}, + {"smsw", Op_DstRMWNT}, + {"sqrtpd", Op_DstSrcSSE}, + {"sqrtps", Op_DstSrcSSE}, + {"sqrtsd", Op_DstSrcSSE}, + {"sqrtss", Op_DstSrcSSE}, + {"stc", Op_Flags}, + {"std", Op_Flags}, + {"sti", Op_Flags}, + {"stmxcsr", Op_DstMemNT}, + {"stos", Op_stos}, + {"stosb", Op_stosX}, + {"stosd", Op_stosX}, #ifdef ASM_X86_64 - { "stosq", Op_stosX }, + {"stosq", Op_stosX}, #endif - { "stosw", Op_stosX }, - { "str", Op_DstMemNT }, // also r16 - { "sub", Op_UpdSrcF }, - { "subpd", Op_DstSrcSSE }, - { "subps", Op_DstSrcSSE }, + {"stosw", Op_stosX}, + {"str", Op_DstMemNT}, // also r16 + {"sub", Op_UpdSrcF}, + {"subpd", Op_DstSrcSSE}, + {"subps", Op_DstSrcSSE}, #ifdef ASM_X86_64 - { "subq", Op_DstSrcSSE }, // ? + {"subq", Op_DstSrcSSE}, // ? #endif - { "subsd", Op_DstSrcSSE }, - { "subss", Op_DstSrcSSE }, + {"subsd", Op_DstSrcSSE}, + {"subss", Op_DstSrcSSE}, #ifdef ASM_X86_64 - { "swapgs", Op_0 }, // Not supported by DMD + {"swapgs", Op_0}, // Not supported by DMD #endif - { "syscall", Op_0 }, - { "sysenter", Op_0 }, - { "sysexit", Op_0 }, - { "sysret", Op_0 }, + {"syscall", Op_0}, + {"sysenter", Op_0}, + {"sysexit", Op_0}, + {"sysret", Op_0}, #ifdef ASM_X86_64 - { "sysretq", Op_0 }, // ? + {"sysretq", Op_0}, // ? #endif - { "test", Op_SrcSrcF }, - { "ucomisd", Op_SrcSrcSSEF }, - { "ucomiss", Op_SrcSrcSSEF }, - { "ud2", Op_0 }, - { "unpckhpd", Op_DstSrcSSE }, - { "unpckhps", Op_DstSrcSSE }, - { "unpcklpd", Op_DstSrcSSE }, - { "unpcklps", Op_DstSrcSSE }, -// { vaddpd, XXXX }, -// { vaddps, XXXX }, -// { vaddsd, XXXX }, -// { vaddss, XXXX }, -// { vaddsubpd, XXXX }, -// { vaddsubps, XXXX }, -// { vaesdec, XXXX }, -// { vaesdeclast, XXXX }, -// { vaesenc, XXXX }, -// { vaesenclast, XXXX }, -// { vaesimc, XXXX }, -// { vaeskeygenassist, XXXX }, -// { vandnpd, XXXX }, -// { vandnps, XXXX }, -// { vandpd, XXXX }, -// { vandps, XXXX }, -// { vblendpd, XXXX }, -// { vblendps, XXXX }, -// { vblendvpd, XXXX }, -// { vblendvps, XXXX }, -// { vbroadcastf128, XXXX }, -// { vbroadcastsd, XXXX }, -// { vbroadcastss, XXXX }, -// { vcmppd, XXXX }, -// { vcmpps, XXXX }, -// { vcmpsd, XXXX }, -// { vcmpss, XXXX }, -// { vcomisd, XXXX }, -// { vcomiss, XXXX }, -// { vcvtdq2pd, XXXX }, -// { vcvtdq2ps, XXXX }, -// { vcvtpd2dq, XXXX }, -// { vcvtpd2ps, XXXX }, -// { vcvtph2ps, XXXX }, -// { vcvtps2dq, XXXX }, -// { vcvtps2pd, XXXX }, -// { vcvtps2ph, XXXX }, -// { vcvtsd2si, XXXX }, -// { vcvtsd2ss, XXXX }, -// { vcvtsi2sd, XXXX }, -// { vcvtsi2ss, XXXX }, -// { vcvtss2sd, XXXX }, -// { vcvtss2si, XXXX }, -// { vcvttpd2dq, XXXX }, -// { vcvttps2dq, XXXX }, -// { vcvttsd2si, XXXX }, -// { vcvttss2si, XXXX }, -// { vdivpd, XXXX }, -// { vdivps, XXXX }, -// { vdivsd, XXXX }, -// { vdivss, XXXX }, -// { vdppd, XXXX }, -// { vdpps, XXXX }, - { "verr", Op_SrcMemNTF }, - { "verw", Op_SrcMemNTF }, + {"test", Op_SrcSrcF}, + {"ucomisd", Op_SrcSrcSSEF}, + {"ucomiss", Op_SrcSrcSSEF}, + {"ud2", Op_0}, + {"unpckhpd", Op_DstSrcSSE}, + {"unpckhps", Op_DstSrcSSE}, + {"unpcklpd", Op_DstSrcSSE}, + {"unpcklps", Op_DstSrcSSE}, + // { vaddpd, XXXX }, + // { vaddps, XXXX }, + // { vaddsd, XXXX }, + // { vaddss, XXXX }, + // { vaddsubpd, XXXX }, + // { vaddsubps, XXXX }, + // { vaesdec, XXXX }, + // { vaesdeclast, XXXX }, + // { vaesenc, XXXX }, + // { vaesenclast, XXXX }, + // { vaesimc, XXXX }, + // { vaeskeygenassist, XXXX }, + // { vandnpd, XXXX }, + // { vandnps, XXXX }, + // { vandpd, XXXX }, + // { vandps, XXXX }, + // { vblendpd, XXXX }, + // { vblendps, XXXX }, + // { vblendvpd, XXXX }, + // { vblendvps, XXXX }, + // { vbroadcastf128, XXXX }, + // { vbroadcastsd, XXXX }, + // { vbroadcastss, XXXX }, + // { vcmppd, XXXX }, + // { vcmpps, XXXX }, + // { vcmpsd, XXXX }, + // { vcmpss, XXXX }, + // { vcomisd, XXXX }, + // { vcomiss, XXXX }, + // { vcvtdq2pd, XXXX }, + // { vcvtdq2ps, XXXX }, + // { vcvtpd2dq, XXXX }, + // { vcvtpd2ps, XXXX }, + // { vcvtph2ps, XXXX }, + // { vcvtps2dq, XXXX }, + // { vcvtps2pd, XXXX }, + // { vcvtps2ph, XXXX }, + // { vcvtsd2si, XXXX }, + // { vcvtsd2ss, XXXX }, + // { vcvtsi2sd, XXXX }, + // { vcvtsi2ss, XXXX }, + // { vcvtss2sd, XXXX }, + // { vcvtss2si, XXXX }, + // { vcvttpd2dq, XXXX }, + // { vcvttps2dq, XXXX }, + // { vcvttsd2si, XXXX }, + // { vcvttss2si, XXXX }, + // { vdivpd, XXXX }, + // { vdivps, XXXX }, + // { vdivsd, XXXX }, + // { vdivss, XXXX }, + // { vdppd, XXXX }, + // { vdpps, XXXX }, + {"verr", Op_SrcMemNTF}, + {"verw", Op_SrcMemNTF}, // { vextractf128, XXXX }, // { vextractps, XXXX }, // { vfmadd132pd, XXXX }, @@ -1778,2082 +1936,1860 @@ namespace AsmParserx8664 // { vzeroall, XXXX }, // { vzeroupper, XXXX }, #ifndef ASM_X86_64 - { "wait", Op_0 }, + {"wait", Op_0}, #endif - { "wbinvd", Op_0 }, -// { wrfsbase, XXXX }, -// { wrgsbase, XXXX }, - { "wrmsr", Op_0 }, - { "xadd", Op_UpdUpdF }, - { "xchg", Op_UpdUpd }, - { "xgetbv", Op_xgetbv }, - { "xlat", Op_xlat }, - { "xlatb", Op_0_AX }, - { "xor", Op_DstSrcF }, - { "xorpd", Op_DstSrcSSE }, - { "xorps", Op_DstSrcSSE }, + {"wbinvd", Op_0}, + // { wrfsbase, XXXX }, + // { wrgsbase, XXXX }, + {"wrmsr", Op_0}, + {"xadd", Op_UpdUpdF}, + {"xchg", Op_UpdUpd}, + {"xgetbv", Op_xgetbv}, + {"xlat", Op_xlat}, + {"xlatb", Op_0_AX}, + {"xor", Op_DstSrcF}, + {"xorpd", Op_DstSrcSSE}, + {"xorps", Op_DstSrcSSE}, #ifdef ASM_X86_64 - { "xorq", Op_DstSrcNT }, //? + {"xorq", Op_DstSrcNT}, //? #endif -// { xrstor, XXXX }, -// { xrstor64, XXXX }, -// { xsave, XXXX }, -// { xsave64, XXXX }, -// { xsaveopt, XXXX }, -// { xsaveopt64, XXXX }, -// { xsetbv, XXXX }, - }; + // { xrstor, XXXX }, + // { xrstor64, XXXX }, + // { xsave, XXXX }, + // { xsave64, XXXX }, + // { xsaveopt, XXXX }, + // { xsaveopt64, XXXX }, + // { xsetbv, XXXX }, +}; - typedef enum - { - Default_Ptr = 0, - Byte_Ptr = 1, - Short_Ptr = 2, - Int_Ptr = 4, - QWord_Ptr = 8, - Float_Ptr = 4, - Double_Ptr = 8, - Extended_Ptr = 10, - Near_Ptr = 98, - Far_Ptr = 99, - N_PtrTypes - } PtrType; +typedef enum { + Default_Ptr = 0, + Byte_Ptr = 1, + Short_Ptr = 2, + Int_Ptr = 4, + QWord_Ptr = 8, + Float_Ptr = 4, + Double_Ptr = 8, + Extended_Ptr = 10, + Near_Ptr = 98, + Far_Ptr = 99, + N_PtrTypes +} PtrType; - static const int N_PtrNames = 8; - static const char * ptrTypeNameTable[N_PtrNames] = - { - "word", "dword", "qword", - "float", "double", "extended", - "near", "far" - }; +static const int N_PtrNames = 8; +static const char *ptrTypeNameTable[N_PtrNames] = { + "word", "dword", "qword", "float", "double", "extended", "near", "far"}; - static Identifier * ptrTypeIdentTable[N_PtrNames]; - static PtrType ptrTypeValueTable[N_PtrNames] = - { - Short_Ptr, Int_Ptr, QWord_Ptr, - Float_Ptr, Double_Ptr, Extended_Ptr, - Near_Ptr, Far_Ptr - }; +static Identifier *ptrTypeIdentTable[N_PtrNames]; +static PtrType ptrTypeValueTable[N_PtrNames] = { + Short_Ptr, Int_Ptr, QWord_Ptr, Float_Ptr, + Double_Ptr, Extended_Ptr, Near_Ptr, Far_Ptr}; - typedef enum - { - Opr_Invalid, - Opr_Immediate, - Opr_Reg, - Opr_Mem - } OperandClass; +typedef enum { Opr_Invalid, Opr_Immediate, Opr_Reg, Opr_Mem } OperandClass; - /* kill inlining if we reference a local? */ +/* kill inlining if we reference a local? */ - /* DMD seems to allow only one 'symbol' per operand .. include __LOCAL_SIZE */ +/* DMD seems to allow only one 'symbol' per operand .. include __LOCAL_SIZE */ - /* DMD offset usage: [] seems to always be relative to EBP+8 .. even - if naked.. */ +/* DMD offset usage: [] seems to always be relative to EBP+8 .. even + if naked.. */ // mov eax, 4 // mov eax, fs:4 // -- have to assume we know whether or not to use '$' - static Token eof_tok; - static Expression * Handled; - static Identifier * ident_seg; +static Token eof_tok; +static Expression *Handled; +static Identifier *ident_seg; - struct AsmProcessor +struct AsmProcessor { + typedef struct { + int inBracket; + int hasBracket; + int hasNumber; + int isOffset; + + Reg segmentPrefix; + Reg reg; + sinteger_t constDisplacement; // use to build up.. should be int constant in + // the end.. + Expressions symbolDisplacement; // array of expressions or.. + Reg baseReg; + Reg indexReg; + int scale; + + OperandClass cls; + PtrType dataSize; + PtrType dataSizeHint; // DMD can use the type of a referenced variable + } Operand; + + static const unsigned Max_Operands = 3; + + AsmStatement *stmt; + Scope *sc; + + Token *token; + std::ostringstream insnTemplate; + + AsmOp op; + AsmOpInfo *opInfo; + Operand operands[Max_Operands]; + Identifier *opIdent; + Operand *operand; + + AsmProcessor(Scope *sc, AsmStatement *stmt) { + this->sc = sc; + this->stmt = stmt; + token = stmt->tokens; + + opInfo = NULL; + + if (!regInfo[0].ident) { + char buf[8], *p; + + for (int i = 0; i < N_Regs; i++) { + strncpy(buf, regInfo[i].name, sizeof(buf) - 1); + for (p = buf; *p; p++) + *p = std::tolower(*p); + regInfo[i].gccName = std::string(buf, p - buf); + if ((i <= Reg_ST || i > Reg_ST7) && i != Reg_EFLAGS) + regInfo[i].ident = Identifier::idPool(regInfo[i].name); + } + + for (int i = 0; i < N_PtrNames; i++) + ptrTypeIdentTable[i] = Identifier::idPool(ptrTypeNameTable[i]); + + Handled = new Expression(Loc(), TOKvoid, sizeof(Expression)); + + ident_seg = Identifier::idPool("seg"); + + eof_tok.value = TOKeof; + eof_tok.next = 0; + } + } + + void run() { parse(); } + + void nextToken() { + if (token->next) + token = token->next; + else + token = &eof_tok; + } + + Token *peekToken() { + if (token->next) + return token->next; + else + return &eof_tok; + } + + void expectEnd() { + if (token->value != TOKeof) + stmt->error("expected end of statement"); // %% extra at end... + } + + void parse() { + op = parseOpcode(); + + switch (op) { + case Op_Align: + doAlign(); + expectEnd(); + break; + case Op_Even: + doEven(); + expectEnd(); + break; + case Op_Naked: + doNaked(); + expectEnd(); + break; + case Op_Invalid: + break; + default: + if (op >= Op_db && op <= Op_de) + doData(); + else + doInstruction(); + } + } + + AsmOp parseOpcode() { + static const int N_ents = sizeof(opData) / sizeof(AsmOpEnt); + + switch (token->value) { + case TOKalign: + nextToken(); + return Op_Align; + case TOKin: + nextToken(); + opIdent = Id::___in; + return Op_in; + case TOKint32: // "int" + nextToken(); + opIdent = Id::__int; + return Op_SrcImm; + case TOKout: + nextToken(); + opIdent = Id::___out; + return Op_out; + case TOKidentifier: + // search for mnemonic below + break; + default: + stmt->error("expected opcode"); + return Op_Invalid; + } + + opIdent = token->ident; + const char *opcode = token->ident->string; + + nextToken(); + + // %% okay to use bsearch? + int i = 0, j = N_ents, k, l; + do { + k = (i + j) / 2; + l = strcmp(opcode, opData[k].inMnemonic); + if (!l) + return opData[k].asmOp; + else if (l < 0) + j = k; + else + i = k + 1; + } while (i != j); + + stmt->error("unknown opcode '%s'", opcode); + + return Op_Invalid; + } + + // need clobber information.. use information is good too... + void doInstruction() { + unsigned operand_i = 0; + + opInfo = &asmOpInfo[op]; + memset(operands, 0, sizeof(operands)); + + if (token->value == TOKeof && op == Op_FMath0) { +// no iteration for x86 vs. single iteration for x64 +#ifndef ASM_X86_64 + while (false) +#else + for (operand_i = 0; operand_i < 1; operand_i++) +#endif + { + operand = &operands[operand_i]; + operand->reg = operand->baseReg = operand->indexReg = + operand->segmentPrefix = Reg_Invalid; + operand->cls = Opr_Reg; + if (operand_i == 0) { + operand->reg = Reg_ST; + } else { + operand->reg = Reg_ST1; + } + operand->hasNumber = 0; + operand->constDisplacement = 0; + parseOperand(); + + if (matchOperands(operand_i)) { + AsmCode *asmcode = new AsmCode(N_Regs); + + if (formatInstruction(operand_i, asmcode)) + stmt->asmcode = (code *)asmcode; + } + } + return; + } + + while (token->value != TOKeof) { + if (operand_i < Max_Operands) { + operand = &operands[operand_i]; + operand->reg = operand->baseReg = operand->indexReg = + operand->segmentPrefix = Reg_Invalid; + parseOperand(); + operand_i++; + } else { + stmt->error("too many operands for instruction"); + break; + } + + if (token->value == TOKcomma) { + nextToken(); + } else if (token->value != TOKeof) { + stmt->error("end of instruction expected, not '%s'", token->toChars()); + return; + } + } + // if (operand_i < opInfo->minOperands) { + // stmt->error("too few operands for instruction"); + // } + + if (matchOperands(operand_i)) { + AsmCode *asmcode = new AsmCode(N_Regs); + + if (formatInstruction(operand_i, asmcode)) + stmt->asmcode = (code *)asmcode; + } + } + + void setAsmCode() { + AsmCode *asmcode = new AsmCode(N_Regs); + asmcode->insnTemplate = insnTemplate.str(); + Logger::cout() << "insnTemplate = " << asmcode->insnTemplate << '\n'; + stmt->asmcode = (code *)asmcode; + } + + // note: doesn't update AsmOp op + bool matchOperands(unsigned nOperands) { + bool wrong_number = true; + + for (unsigned i = 0; i < nOperands; i++) + classifyOperand(&operands[i]); + + while (1) { + if (nOperands == opInfo->nOperands()) { + wrong_number = false; + /* Cases in which number of operands is not + enough for a match: Op_FCmp/Op_FCmp1, + Op_FCmpP/Op_FCmpP1 */ + for (unsigned i = 0; i < nOperands; i++) { + Operand *operand = &operands[i]; + + switch (opInfo->operands[i] & Opr_ClassMask) { + case OprC_Mem: // no FPMem currently + if (operand->cls != Opr_Mem) + goto no_match; + break; + case OprC_RFP: + if (!(operand->reg >= Reg_ST && operand->reg <= Reg_ST7)) + goto no_match; + break; + default: + break; + } + } + + return true; + } + no_match: + if (opInfo->linkType == Next_Form) + opInfo = &asmOpInfo[op = (AsmOp)opInfo->link]; + else + break; + } + if (wrong_number) + stmt->error("wrong number of operands"); + else + stmt->error("wrong operand types"); + return false; + } + + // OSX and 32-bit Windows need an extra leading underscore when mangling a + // symbol name. + static bool prependExtraUnderscore() { + return global.params.targetTriple.getOS() == llvm::Triple::MacOSX || + global.params.targetTriple.getOS() == llvm::Triple::Darwin || + (global.params.targetTriple.isOSWindows() && + global.params.targetTriple.isArch32Bit()); + } + + void addOperand(const char *fmt, AsmArgType type, Expression *e, + AsmCode *asmcode, AsmArgMode mode = Mode_Input) { + if (sc->func->naked) { + switch (type) { + case Arg_Integer: + if (e->type->isunsigned()) + insnTemplate << "$" << e->toUInteger(); + else +#ifndef ASM_X86_64 + insnTemplate << "$" << (sinteger_t)e->toInteger(); +#else + insnTemplate << "$" << e->toInteger(); +#endif + break; + + case Arg_Pointer: + stmt->error("unsupported pointer reference to '%s' in naked asm", + e->toChars()); + break; + + case Arg_Memory: + // Peel off one layer of explicitly taking the address, if present. + if (e->op == TOKaddress) { + e = static_cast(e)->e1; + } + + if (e->op == TOKvar) { + VarExp *v = (VarExp *)e; + if (VarDeclaration *vd = v->var->isVarDeclaration()) { + if (!vd->isDataseg()) { + stmt->error("only global variables can be referenced by " + "identifier in naked asm"); + break; + } + + // print out the mangle + if (prependExtraUnderscore()) { + insnTemplate << "_"; + } + insnTemplate << mangle(vd); + getIrGlobal(vd, true)->nakedUse = true; + break; + } + } + stmt->error("unsupported memory reference to '%s' in naked asm", + e->toChars()); + break; + + default: + llvm_unreachable("Unsupported argument in asm."); + break; + } + } else { + insnTemplate << fmt << "<<" << (mode == Mode_Input ? "in" : "out") + << asmcode->args.size() << ">>"; + asmcode->args.push_back(AsmArg(type, e, mode)); + } + } + void addOperand2(const char *fmtpre, const char *fmtpost, AsmArgType type, + Expression *e, AsmCode *asmcode, + AsmArgMode mode = Mode_Input) { + if (sc->func->naked) { + // taken from above + stmt->error( + "only global variables can be referenced by identifier in naked asm"); + return; + } + + insnTemplate << fmtpre << "<<" << (mode == Mode_Input ? "in" : "out") + << ">>" << fmtpost; + asmcode->args.push_back(AsmArg(type, e, mode)); + } + + void addLabel(char *id) { + // We need to delay emitting the actual function name, see + // replace_func_name in asmstmt.cpp for details. + printLabelName(insnTemplate, "<>", id); + } + + /* Determines whether the operand is a register, memory reference + or immediate. Immediate addresses are currently classified as + memory. This function is called before the exact instructions + is known and thus, should not use opInfo. */ + void classifyOperand(Operand *operand) { + operand->cls = classifyOperand1(operand); + } + + OperandClass classifyOperand1(Operand *operand) { + bool is_localsize = false; + bool really_have_symbol = false; + + if (operand->symbolDisplacement.dim) { + is_localsize = + isLocalSize((Expression *)operand->symbolDisplacement.data[0]); + really_have_symbol = !is_localsize; + } + + if (operand->isOffset && !operand->hasBracket) + return Opr_Immediate; + + if (operand->hasBracket || + really_have_symbol) // %% redo for 'offset' function { - typedef struct - { - int inBracket; - int hasBracket; - int hasNumber; - int isOffset; + if (operand->reg != Reg_Invalid) { + invalidExpression(); + return Opr_Invalid; + } - Reg segmentPrefix; - Reg reg; - sinteger_t constDisplacement; // use to build up.. should be int constant in the end.. - Expressions symbolDisplacement; // array of expressions or.. - Reg baseReg; - Reg indexReg; - int scale; + return Opr_Mem; + } - OperandClass cls; - PtrType dataSize; - PtrType dataSizeHint; // DMD can use the type of a referenced variable - } Operand; + if (operand->reg != Reg_Invalid && operand->constDisplacement != 0) { + invalidExpression(); + return Opr_Invalid; + } - static const unsigned Max_Operands = 3; + if (operand->segmentPrefix != Reg_Invalid) { + if (operand->reg != Reg_Invalid) { + invalidExpression(); + return Opr_Invalid; + } - AsmStatement * stmt; - Scope * sc; + return Opr_Mem; + } - Token * token; - std::ostringstream insnTemplate; + if (operand->reg != Reg_Invalid && !operand->hasNumber) + return Opr_Reg; - AsmOp op; - AsmOpInfo * opInfo; - Operand operands[Max_Operands]; - Identifier * opIdent; - Operand * operand; - - AsmProcessor ( Scope * sc, AsmStatement * stmt ) - { - this->sc = sc; - this->stmt = stmt; - token = stmt->tokens; - - opInfo = NULL; - - if ( ! regInfo[0].ident ) - { - char buf[8], *p; - - for ( int i = 0; i < N_Regs; i++ ) - { - strncpy ( buf, regInfo[i].name, sizeof ( buf ) - 1 ); - for ( p = buf; *p; p++ ) - *p = std::tolower ( *p ); - regInfo[i].gccName = std::string ( buf, p - buf ); - if ( ( i <= Reg_ST || i > Reg_ST7 ) && i != Reg_EFLAGS ) - regInfo[i].ident = Identifier::idPool ( regInfo[i].name ); - } - - for ( int i = 0; i < N_PtrNames; i++ ) - ptrTypeIdentTable[i] = Identifier::idPool(ptrTypeNameTable[i]); - - Handled = new Expression ( Loc(), TOKvoid, sizeof ( Expression ) ); - - ident_seg = Identifier::idPool("seg"); - - eof_tok.value = TOKeof; - eof_tok.next = 0; - } - } - - void run() - { - parse(); - } - - void nextToken() - { - if ( token->next ) - token = token->next; - else - token = & eof_tok; - } - - Token * peekToken() - { - if ( token->next ) - return token->next; - else - return & eof_tok; - } - - void expectEnd() - { - if ( token->value != TOKeof ) - stmt->error ( "expected end of statement" ); // %% extra at end... - } - - void parse() - { - op = parseOpcode(); - - switch ( op ) - { - case Op_Align: - doAlign(); - expectEnd(); - break; - case Op_Even: - doEven(); - expectEnd(); - break; - case Op_Naked: - doNaked(); - expectEnd(); - break; - case Op_Invalid: - break; - default: - if ( op >= Op_db && op <= Op_de ) - doData(); - else - doInstruction(); - } - } - - AsmOp parseOpcode() - { - static const int N_ents = sizeof ( opData ) /sizeof ( AsmOpEnt ); - - switch ( token->value ) - { - case TOKalign: - nextToken(); - return Op_Align; - case TOKin: - nextToken(); - opIdent = Id::___in; - return Op_in; - case TOKint32: // "int" - nextToken(); - opIdent = Id::__int; - return Op_SrcImm; - case TOKout: - nextToken(); - opIdent = Id::___out; - return Op_out; - case TOKidentifier: - // search for mnemonic below - break; - default: - stmt->error ( "expected opcode" ); - return Op_Invalid; - } - - opIdent = token->ident; - const char * opcode = token->ident->string; - - nextToken(); - - // %% okay to use bsearch? - int i = 0, j = N_ents, k, l; - do - { - k = ( i + j ) / 2; - l = strcmp ( opcode, opData[k].inMnemonic ); - if ( ! l ) - return opData[k].asmOp; - else if ( l < 0 ) - j = k; - else - i = k + 1; - } - while ( i != j ); - - stmt->error ( "unknown opcode '%s'", opcode ); - - return Op_Invalid; - } - - // need clobber information.. use information is good too... - void doInstruction() - { - unsigned operand_i = 0; - - opInfo = & asmOpInfo[op]; - memset ( operands, 0, sizeof ( operands ) ); - - if ( token->value == TOKeof && op == Op_FMath0 ) - { - // no iteration for x86 vs. single iteration for x64 + // should check immediate given (operand->hasNumber); + // + if (operand->hasNumber || is_localsize) { + // size determination not correct if there are symbols Opr_Immediate + if (operand->dataSize == Default_Ptr) { + if (operand->constDisplacement < 0x100) + operand->dataSize = Byte_Ptr; + else if (operand->constDisplacement < 0x10000) + operand->dataSize = Short_Ptr; #ifndef ASM_X86_64 - while (false) + else + operand->dataSize = Int_Ptr; #else - for (operand_i = 0; operand_i < 1; operand_i++) + else if (operand->constDisplacement <= 0xFFFFFFFF) + operand->dataSize = Int_Ptr; + else + // This could be possible since we are using 48 bits + operand->dataSize = QWord_Ptr; #endif - { - operand = & operands[operand_i]; - operand->reg = operand->baseReg = operand->indexReg = - operand->segmentPrefix = Reg_Invalid; - operand->cls = Opr_Reg; - if ( operand_i == 0 ) - { - operand->reg = Reg_ST; - } - else - { - operand->reg = Reg_ST1; - } - operand->hasNumber = 0; - operand->constDisplacement = 0; - parseOperand(); + } + return Opr_Immediate; + } - if ( matchOperands ( operand_i ) ) - { - AsmCode * asmcode = new AsmCode ( N_Regs ); + // probably a bug,? + stmt->error("invalid operand"); + return Opr_Invalid; + } - if ( formatInstruction ( operand_i, asmcode ) ) - stmt->asmcode = ( code * ) asmcode; - } - } - return; - } + void writeReg(Reg reg) { insnTemplate << "%" << regInfo[reg].gccName; } - while ( token->value != TOKeof ) - { - if ( operand_i < Max_Operands ) - { - operand = & operands[operand_i]; - operand->reg = operand->baseReg = operand->indexReg = - operand->segmentPrefix = Reg_Invalid; - parseOperand(); - operand_i++; - } - else - { - stmt->error ( "too many operands for instruction" ); - break; - } + bool opTakesLabel() { + switch (op) { + case Op_Branch: + case Op_CBranch: + case Op_Loop: + return true; + default: + return false; + } + } - if ( token->value == TOKcomma ) - { - nextToken(); - } - else if ( token->value != TOKeof ) - { - stmt->error ( "end of instruction expected, not '%s'", token->toChars() ); - return; - } - } -// if (operand_i < opInfo->minOperands) { -// stmt->error("too few operands for instruction"); -// } - - if ( matchOperands ( operand_i ) ) - { - AsmCode * asmcode = new AsmCode ( N_Regs ); - - if ( formatInstruction ( operand_i, asmcode ) ) - stmt->asmcode = ( code * ) asmcode; - } - } - - void setAsmCode() - { - AsmCode * asmcode = new AsmCode ( N_Regs ); - asmcode->insnTemplate = insnTemplate.str(); - Logger::cout() << "insnTemplate = " << asmcode->insnTemplate << '\n'; - stmt->asmcode = ( code* ) asmcode; - } - - // note: doesn't update AsmOp op - bool matchOperands ( unsigned nOperands ) - { - bool wrong_number = true; - - for ( unsigned i = 0; i < nOperands; i++ ) - classifyOperand ( & operands[i] ); - - while ( 1 ) - { - if ( nOperands == opInfo->nOperands() ) - { - wrong_number = false; - /* Cases in which number of operands is not - enough for a match: Op_FCmp/Op_FCmp1, - Op_FCmpP/Op_FCmpP1 */ - for ( unsigned i = 0; i < nOperands; i++ ) - { - Operand * operand = & operands[i]; - - switch ( opInfo->operands[i] & Opr_ClassMask ) - { - case OprC_Mem: // no FPMem currently - if ( operand->cls != Opr_Mem ) - goto no_match; - break; - case OprC_RFP: - if ( ! ( operand->reg >= Reg_ST && operand->reg <= Reg_ST7 ) ) - goto no_match; - break; - default: - break; - } - } - - return true; - } - no_match: - if ( opInfo->linkType == Next_Form ) - opInfo = & asmOpInfo[ op = ( AsmOp ) opInfo->link ]; - else - break; - } - if ( wrong_number ) - stmt->error ( "wrong number of operands" ); - else - stmt->error ( "wrong operand types" ); - return false; - } - - // OSX and 32-bit Windows need an extra leading underscore when mangling a symbol name. - static bool prependExtraUnderscore() - { - return global.params.targetTriple.getOS() == llvm::Triple::MacOSX - || global.params.targetTriple.getOS() == llvm::Triple::Darwin - || ( global.params.targetTriple.isOSWindows() && global.params.targetTriple.isArch32Bit() ); - } - - void addOperand ( const char * fmt, AsmArgType type, Expression * e, AsmCode * asmcode, AsmArgMode mode = Mode_Input ) - { - if ( sc->func->naked ) - { - switch ( type ) - { - case Arg_Integer: - if ( e->type->isunsigned() ) - insnTemplate << "$" << e->toUInteger(); - else -#ifndef ASM_X86_64 - insnTemplate << "$" << (sinteger_t)e->toInteger(); -#else - insnTemplate << "$" << e->toInteger(); -#endif - break; - - case Arg_Pointer: - stmt->error ( "unsupported pointer reference to '%s' in naked asm", e->toChars() ); - break; - - case Arg_Memory: - // Peel off one layer of explicitly taking the address, if present. - if ( e->op == TOKaddress ) - { - e = static_cast(e)->e1; - } - - if ( e->op == TOKvar ) - { - VarExp* v = ( VarExp* ) e; - if ( VarDeclaration* vd = v->var->isVarDeclaration() ) - { - if ( !vd->isDataseg() ) - { - stmt->error ( "only global variables can be referenced by identifier in naked asm" ); - break; - } - - // print out the mangle - if ( prependExtraUnderscore() ) - { - insnTemplate << "_"; - } - insnTemplate << mangle(vd); - getIrGlobal(vd, true)->nakedUse = true; - break; - } - } - stmt->error ( "unsupported memory reference to '%s' in naked asm", e->toChars() ); - break; - - default: - llvm_unreachable("Unsupported argument in asm."); - break; - } - } - else - { - insnTemplate << fmt - << "<<" << (mode==Mode_Input ? "in" : "out") << asmcode->args.size() << ">>"; - asmcode->args.push_back ( AsmArg ( type, e, mode ) ); - } - } - void addOperand2 ( const char * fmtpre, const char * fmtpost, AsmArgType type, Expression * e, AsmCode * asmcode, AsmArgMode mode = Mode_Input ) - { - if ( sc->func->naked ) - { - // taken from above - stmt->error ( "only global variables can be referenced by identifier in naked asm" ); - return; - } - - insnTemplate << fmtpre - << "<<" << (mode==Mode_Input ? "in" : "out") << ">>" - << fmtpost; - asmcode->args.push_back ( AsmArg ( type, e, mode ) ); - } - - void addLabel ( char* id ) - { - // We need to delay emitting the actual function name, see - // replace_func_name in asmstmt.cpp for details. - printLabelName(insnTemplate, "<>", id); - } - - /* Determines whether the operand is a register, memory reference - or immediate. Immediate addresses are currently classified as - memory. This function is called before the exact instructions - is known and thus, should not use opInfo. */ - void classifyOperand ( Operand * operand ) - { - operand->cls = classifyOperand1 ( operand ); - } - - OperandClass classifyOperand1 ( Operand * operand ) - { - bool is_localsize = false; - bool really_have_symbol = false; - - if ( operand->symbolDisplacement.dim ) - { - is_localsize = isLocalSize ( ( Expression * ) operand->symbolDisplacement.data[0] ); - really_have_symbol = ! is_localsize; - } - - if ( operand->isOffset && ! operand->hasBracket ) - return Opr_Immediate; - - if ( operand->hasBracket || really_have_symbol ) // %% redo for 'offset' function - { - if ( operand->reg != Reg_Invalid ) - { - invalidExpression(); - return Opr_Invalid; - } - - return Opr_Mem; - } - - if ( operand->reg != Reg_Invalid && operand->constDisplacement != 0 ) - { - invalidExpression(); - return Opr_Invalid; - } - - if ( operand->segmentPrefix != Reg_Invalid ) - { - if ( operand->reg != Reg_Invalid ) - { - invalidExpression(); - return Opr_Invalid; - } - - return Opr_Mem; - } - - if ( operand->reg != Reg_Invalid && ! operand->hasNumber ) - return Opr_Reg; - - // should check immediate given (operand->hasNumber); - // - if ( operand->hasNumber || is_localsize ) - { - // size determination not correct if there are symbols Opr_Immediate - if ( operand->dataSize == Default_Ptr ) - { - if ( operand->constDisplacement < 0x100 ) - operand->dataSize = Byte_Ptr; - else if ( operand->constDisplacement < 0x10000 ) - operand->dataSize = Short_Ptr; -#ifndef ASM_X86_64 - else - operand->dataSize = Int_Ptr; -#else - else if ( operand->constDisplacement <= 0xFFFFFFFF ) - operand->dataSize = Int_Ptr; - else - //This could be possible since we are using 48 bits - operand->dataSize = QWord_Ptr; -#endif - } - return Opr_Immediate; - } - - // probably a bug,? - stmt->error ( "invalid operand" ); - return Opr_Invalid; - } - - void writeReg ( Reg reg ) - { - insnTemplate << "%" << regInfo[reg].gccName; - } - - bool opTakesLabel() - { - switch ( op ) - { - case Op_Branch: - case Op_CBranch: - case Op_Loop: - return true; - default: - return false; - } - } - - bool getTypeSuffix ( TypeNeeded needed, PtrType ptrtype, std::string & type_suffix ) - { - switch ( needed ) - { - case Byte_NoType: - return ptrtype == Byte_Ptr; - case Word_Types: - if ( ptrtype == Byte_Ptr ) - return false; - // drop through - case Int_Types: - switch ( ptrtype ) - { - case Byte_Ptr: type_suffix = 'b'; break; - case Short_Ptr: type_suffix = 'w'; break; - case Int_Ptr: type_suffix = 'l'; break; + bool getTypeSuffix(TypeNeeded needed, PtrType ptrtype, + std::string &type_suffix) { + switch (needed) { + case Byte_NoType: + return ptrtype == Byte_Ptr; + case Word_Types: + if (ptrtype == Byte_Ptr) + return false; + // drop through + case Int_Types: + switch (ptrtype) { + case Byte_Ptr: + type_suffix = 'b'; + break; + case Short_Ptr: + type_suffix = 'w'; + break; + case Int_Ptr: + type_suffix = 'l'; + break; #ifdef ASM_X86_64 - case QWord_Ptr: type_suffix = 'q'; break; + case QWord_Ptr: + type_suffix = 'q'; + break; #endif - default: - // %% these may be too strict - return false; - } - break; - case FPInt_Types: - switch ( ptrtype ) - { - case Short_Ptr: type_suffix = ""; break; - case Int_Ptr: type_suffix = 'l'; break; - case QWord_Ptr: type_suffix = "ll"; break; - default: - return false; - } - break; - case FP_Types: - switch ( ptrtype ) - { - case Float_Ptr: type_suffix = 's'; break; - case Double_Ptr: type_suffix = 'l'; break; - case Extended_Ptr: - // MS C runtime: real = 64-bit double - if (global.params.targetTriple.isWindowsMSVCEnvironment()) - type_suffix = 'l'; - else - type_suffix = 't'; - break; - default: - return false; - } - break; - default: - return false; - } - return true; - } + default: + // %% these may be too strict + return false; + } + break; + case FPInt_Types: + switch (ptrtype) { + case Short_Ptr: + type_suffix = ""; + break; + case Int_Ptr: + type_suffix = 'l'; + break; + case QWord_Ptr: + type_suffix = "ll"; + break; + default: + return false; + } + break; + case FP_Types: + switch (ptrtype) { + case Float_Ptr: + type_suffix = 's'; + break; + case Double_Ptr: + type_suffix = 'l'; + break; + case Extended_Ptr: + // MS C runtime: real = 64-bit double + if (global.params.targetTriple.isWindowsMSVCEnvironment()) + type_suffix = 'l'; + else + type_suffix = 't'; + break; + default: + return false; + } + break; + default: + return false; + } + return true; + } - // also set impl clobbers - bool formatInstruction ( int nOperands, AsmCode * asmcode ) - { - const char *fmt; - const char *mnemonic; - std::string type_suffix; - bool use_star; - AsmArgMode mode; + // also set impl clobbers + bool formatInstruction(int nOperands, AsmCode *asmcode) { + const char *fmt; + const char *mnemonic; + std::string type_suffix; + bool use_star; + AsmArgMode mode; - insnTemplate.str(""); - // %% todo: special case for something.. - if ( opInfo->linkType == Out_Mnemonic ) - mnemonic = alternateMnemonics[opInfo->link]; - else - mnemonic = opIdent->string; + insnTemplate.str(""); + // %% todo: special case for something.. + if (opInfo->linkType == Out_Mnemonic) + mnemonic = alternateMnemonics[opInfo->link]; + else + mnemonic = opIdent->string; - // handle two-operand form where second arg is ignored. - // must be done before type_suffix detection - if ( op == Op_FidR_P || op == Op_fxch || op == Op_FfdRR_P ) - { - if (operands[1].cls == Opr_Reg && operands[1].reg == Reg_ST ) - nOperands = 1; - else - stmt->error("instruction allows only ST as second argument"); - } + // handle two-operand form where second arg is ignored. + // must be done before type_suffix detection + if (op == Op_FidR_P || op == Op_fxch || op == Op_FfdRR_P) { + if (operands[1].cls == Opr_Reg && operands[1].reg == Reg_ST) + nOperands = 1; + else + stmt->error("instruction allows only ST as second argument"); + } #ifdef ASM_X86_64 - if ( op == Op_FCmpFlgP ) - { - // Explicitly add %st as second argument to fucomip – it should - // be implicit, but the GNU as shipping with Mac OS X (Apple Inc - // version cctools-800~26, GNU assembler version 1.38) chokes on - // it otherwise. - assert ( nOperands == 1 ); - nOperands = 2; - operands[1] = operands[0]; - memset ( operands, 0, sizeof( Operand ) ); - operands[0].cls = Opr_Reg; - operands[0].reg = Reg_ST; - } + if (op == Op_FCmpFlgP) { + // Explicitly add %st as second argument to fucomip – it should + // be implicit, but the GNU as shipping with Mac OS X (Apple Inc + // version cctools-800~26, GNU assembler version 1.38) chokes on + // it otherwise. + assert(nOperands == 1); + nOperands = 2; + operands[1] = operands[0]; + memset(operands, 0, sizeof(Operand)); + operands[0].cls = Opr_Reg; + operands[0].reg = Reg_ST; + } #endif - if ( opInfo->needsType ) - { - PtrType exact_type = Default_Ptr; - PtrType min_type = Default_Ptr; - PtrType hint_type = Default_Ptr; + if (opInfo->needsType) { + PtrType exact_type = Default_Ptr; + PtrType min_type = Default_Ptr; + PtrType hint_type = Default_Ptr; - /* Default types: This attempts to match the observed behavior of DMD */ - switch ( opInfo->needsType ) - { - case Int_Types: min_type = Byte_Ptr; break; - case Word_Types: min_type = Short_Ptr; break; - case FPInt_Types: - if ( op == Op_Fis_ST ) // integer math instructions - min_type = Int_Ptr; - else // compare, load, store - min_type = Short_Ptr; - break; - case FP_Types: min_type = Float_Ptr; break; - } - if ( op == Op_push && operands[0].cls == Opr_Immediate ) + /* Default types: This attempts to match the observed behavior of DMD */ + switch (opInfo->needsType) { + case Int_Types: + min_type = Byte_Ptr; + break; + case Word_Types: + min_type = Short_Ptr; + break; + case FPInt_Types: + if (op == Op_Fis_ST) // integer math instructions + min_type = Int_Ptr; + else // compare, load, store + min_type = Short_Ptr; + break; + case FP_Types: + min_type = Float_Ptr; + break; + } + if (op == Op_push && operands[0].cls == Opr_Immediate) #ifndef ASM_X86_64 - min_type = Int_Ptr; + min_type = Int_Ptr; #else - min_type = QWord_Ptr; + min_type = QWord_Ptr; #endif - for ( int i = 0; i < nOperands; i++ ) - { - if ( hint_type == Default_Ptr && - ! ( opInfo->operands[i] & Opr_NoType ) ) - hint_type = operands[i].dataSizeHint; + for (int i = 0; i < nOperands; i++) { + if (hint_type == Default_Ptr && !(opInfo->operands[i] & Opr_NoType)) + hint_type = operands[i].dataSizeHint; - if ( ( opInfo->operands[i] & Opr_NoType ) || - operands[i].dataSize == Default_Ptr ) - continue; - if ( operands[i].cls == Opr_Immediate ) - { - min_type = operands[i].dataSize > min_type ? - operands[i].dataSize : min_type; - } - else - { - exact_type = operands[i].dataSize; // could check for conflicting types - break; - } - } + if ((opInfo->operands[i] & Opr_NoType) || + operands[i].dataSize == Default_Ptr) + continue; + if (operands[i].cls == Opr_Immediate) { + min_type = + operands[i].dataSize > min_type ? operands[i].dataSize : min_type; + } else { + exact_type = + operands[i].dataSize; // could check for conflicting types + break; + } + } - bool type_ok; - if ( exact_type == Default_Ptr ) - { - type_ok = getTypeSuffix ( ( TypeNeeded ) opInfo->needsType, hint_type, type_suffix ); - if ( ! type_ok ) - type_ok = getTypeSuffix ( ( TypeNeeded ) opInfo->needsType, min_type, type_suffix ); - } - else - type_ok = getTypeSuffix ( ( TypeNeeded ) opInfo->needsType, exact_type, type_suffix ); + bool type_ok; + if (exact_type == Default_Ptr) { + type_ok = getTypeSuffix((TypeNeeded)opInfo->needsType, hint_type, + type_suffix); + if (!type_ok) + type_ok = getTypeSuffix((TypeNeeded)opInfo->needsType, min_type, + type_suffix); + } else + type_ok = getTypeSuffix((TypeNeeded)opInfo->needsType, exact_type, + type_suffix); - if ( ! type_ok ) - { - stmt->error ( "invalid operand size" ); - return false; - } - } - else if ( op == Op_Branch ) - { - if ( operands[0].dataSize == Far_Ptr ) // %% type=Far_Ptr not set by Seg:Ofss OTOH, we don't support that.. - insnTemplate << 'l'; - } - else if ( op == Op_FMath0 || op == Op_FdST0ST1 ) - { - operands[0].cls = Opr_Reg; - operands[0].reg = Reg_ST1; - operands[1].cls = Opr_Reg; - operands[1].reg = Reg_ST; - nOperands = 2; - } + if (!type_ok) { + stmt->error("invalid operand size"); + return false; + } + } else if (op == Op_Branch) { + if (operands[0].dataSize == Far_Ptr) // %% type=Far_Ptr not set by + // Seg:Ofss OTOH, we don't support + // that.. + insnTemplate << 'l'; + } else if (op == Op_FMath0 || op == Op_FdST0ST1) { + operands[0].cls = Opr_Reg; + operands[0].reg = Reg_ST1; + operands[1].cls = Opr_Reg; + operands[1].reg = Reg_ST; + nOperands = 2; + } - switch ( op ) - { - case Op_SizedStack: - { - int mlen = strlen ( mnemonic ); - if ( mnemonic[mlen-1] == 'd' ) - insnTemplate.write(mnemonic, mlen-1); - else - { - insnTemplate << mnemonic << 'w'; - } - } - break; - case Op_cmpsd: - case Op_insX: - case Op_lodsX: - case Op_movsd: - case Op_outsX: - case Op_scasX: - case Op_stosX: - { - int mlen = strlen ( mnemonic ); - if ( mnemonic[mlen-1] == 'd' ) - { - insnTemplate.write(mnemonic, mlen-1) << 'l'; - } - else - { - insnTemplate << mnemonic; - } - } - break; - case Op_movsx: - case Op_movzx: - { - char tc_1; - int mlen = strlen ( mnemonic ); - PtrType op1_size = operands[1].dataSize; - if ( op1_size == Default_Ptr ) - op1_size = operands[1].dataSizeHint; - // Need type char for source arg - switch ( op1_size ) - { - case Byte_Ptr: - case Default_Ptr: - tc_1 = 'b'; - break; - case Short_Ptr: - tc_1 = 'w'; - break; - default: - stmt->error ( "invalid operand size/type" ); - return false; - } - assert ( !type_suffix.empty() ); - insnTemplate.write(mnemonic, mlen-1) << tc_1 << type_suffix; - } - break; + switch (op) { + case Op_SizedStack: { + int mlen = strlen(mnemonic); + if (mnemonic[mlen - 1] == 'd') + insnTemplate.write(mnemonic, mlen - 1); + else { + insnTemplate << mnemonic << 'w'; + } + } break; + case Op_cmpsd: + case Op_insX: + case Op_lodsX: + case Op_movsd: + case Op_outsX: + case Op_scasX: + case Op_stosX: { + int mlen = strlen(mnemonic); + if (mnemonic[mlen - 1] == 'd') { + insnTemplate.write(mnemonic, mlen - 1) << 'l'; + } else { + insnTemplate << mnemonic; + } + } break; + case Op_movsx: + case Op_movzx: { + char tc_1; + int mlen = strlen(mnemonic); + PtrType op1_size = operands[1].dataSize; + if (op1_size == Default_Ptr) + op1_size = operands[1].dataSizeHint; + // Need type char for source arg + switch (op1_size) { + case Byte_Ptr: + case Default_Ptr: + tc_1 = 'b'; + break; + case Short_Ptr: + tc_1 = 'w'; + break; + default: + stmt->error("invalid operand size/type"); + return false; + } + assert(!type_suffix.empty()); + insnTemplate.write(mnemonic, mlen - 1) << tc_1 << type_suffix; + } break; - default: - // special case fdiv, fsub: see dmd 840, ldc 256 - if ((strncmp(mnemonic, "fsub", 4) == 0 || - strncmp(mnemonic, "fdiv", 4) == 0) && - operands[0].reg != Reg_ST && op != Op_FMath) - { - // replace: - // f{sub,div}r{p,} <-> f{sub,div}{p,} - if (mnemonic[4] == 'r') - { - insnTemplate.write(mnemonic, 4); - insnTemplate.write(mnemonic+5, strlen(mnemonic)-5); - } - else - { - insnTemplate.write(mnemonic, 4) << "r"; - insnTemplate.write(mnemonic+4, strlen(mnemonic)-4); - } - } - else - { - insnTemplate << mnemonic; - } - // the no-operand versions of floating point ops always pop - if (op == Op_FMath0) - insnTemplate << "p"; - insnTemplate << type_suffix; - break; - } + default: + // special case fdiv, fsub: see dmd 840, ldc 256 + if ((strncmp(mnemonic, "fsub", 4) == 0 || + strncmp(mnemonic, "fdiv", 4) == 0) && + operands[0].reg != Reg_ST && op != Op_FMath) { + // replace: + // f{sub,div}r{p,} <-> f{sub,div}{p,} + if (mnemonic[4] == 'r') { + insnTemplate.write(mnemonic, 4); + insnTemplate.write(mnemonic + 5, strlen(mnemonic) - 5); + } else { + insnTemplate.write(mnemonic, 4) << "r"; + insnTemplate.write(mnemonic + 4, strlen(mnemonic) - 4); + } + } else { + insnTemplate << mnemonic; + } + // the no-operand versions of floating point ops always pop + if (op == Op_FMath0) + insnTemplate << "p"; + insnTemplate << type_suffix; + break; + } - switch ( opInfo->implicitClobbers & Clb_DXAX_Mask ) - { - case Clb_SizeAX: - case Clb_EAX: - asmcode->regs[Reg_EAX] = true; - break; - case Clb_SizeDXAX: - asmcode->regs[Reg_EAX] = true; - if ( type_suffix != "b" ) - asmcode->regs[Reg_EDX] = true; - break; + switch (opInfo->implicitClobbers & Clb_DXAX_Mask) { + case Clb_SizeAX: + case Clb_EAX: + asmcode->regs[Reg_EAX] = true; + break; + case Clb_SizeDXAX: + asmcode->regs[Reg_EAX] = true; + if (type_suffix != "b") + asmcode->regs[Reg_EDX] = true; + break; #ifdef ASM_X86_64 - case Clb_SizeRDXRAX: - asmcode->regs[Reg_RAX] = true; - asmcode->regs[Reg_RDX] = true; - break; + case Clb_SizeRDXRAX: + asmcode->regs[Reg_RAX] = true; + asmcode->regs[Reg_RDX] = true; + break; #endif - default: - // nothing - break; - } + default: + // nothing + break; + } - if ( opInfo->implicitClobbers & Clb_DI ) - asmcode->regs[Reg_EDI] = true; - if ( opInfo->implicitClobbers & Clb_SI ) - asmcode->regs[Reg_ESI] = true; - if ( opInfo->implicitClobbers & Clb_CX ) - asmcode->regs[Reg_ECX] = true; - if ( opInfo->implicitClobbers & Clb_SP ) - asmcode->regs[Reg_ESP] = true; - if ( opInfo->implicitClobbers & Clb_ST ) - { - /* Can't figure out how to tell GCC that an - asm statement leaves an arg pushed on the stack. - Maybe if the statment had and input or output - operand it would work... In any case, clobbering - all FP prevents incorrect code generation. */ - asmcode->regs[Reg_ST] = true; - asmcode->regs[Reg_ST1] = true; - asmcode->regs[Reg_ST2] = true; - asmcode->regs[Reg_ST3] = true; - asmcode->regs[Reg_ST4] = true; - asmcode->regs[Reg_ST5] = true; - asmcode->regs[Reg_ST6] = true; - asmcode->regs[Reg_ST7] = true; - } - if ( opInfo->implicitClobbers & Clb_Flags ) - asmcode->regs[Reg_EFLAGS] = true; - if ( op == Op_cpuid ) - { - asmcode->regs[Reg_EAX] = true; - asmcode->regs[Reg_EBX] = true; - asmcode->regs[Reg_ECX] = true; - asmcode->regs[Reg_EDX] = true; - } + if (opInfo->implicitClobbers & Clb_DI) + asmcode->regs[Reg_EDI] = true; + if (opInfo->implicitClobbers & Clb_SI) + asmcode->regs[Reg_ESI] = true; + if (opInfo->implicitClobbers & Clb_CX) + asmcode->regs[Reg_ECX] = true; + if (opInfo->implicitClobbers & Clb_SP) + asmcode->regs[Reg_ESP] = true; + if (opInfo->implicitClobbers & Clb_ST) { + /* Can't figure out how to tell GCC that an + asm statement leaves an arg pushed on the stack. + Maybe if the statment had and input or output + operand it would work... In any case, clobbering + all FP prevents incorrect code generation. */ + asmcode->regs[Reg_ST] = true; + asmcode->regs[Reg_ST1] = true; + asmcode->regs[Reg_ST2] = true; + asmcode->regs[Reg_ST3] = true; + asmcode->regs[Reg_ST4] = true; + asmcode->regs[Reg_ST5] = true; + asmcode->regs[Reg_ST6] = true; + asmcode->regs[Reg_ST7] = true; + } + if (opInfo->implicitClobbers & Clb_Flags) + asmcode->regs[Reg_EFLAGS] = true; + if (op == Op_cpuid) { + asmcode->regs[Reg_EAX] = true; + asmcode->regs[Reg_EBX] = true; + asmcode->regs[Reg_ECX] = true; + asmcode->regs[Reg_EDX] = true; + } - insnTemplate << ' '; - for ( int i__ = 0; i__ < nOperands; i__++ ) - { - int i; - if ( i__ != 0 ) - insnTemplate << ", "; + insnTemplate << ' '; + for (int i__ = 0; i__ < nOperands; i__++) { + int i; + if (i__ != 0) + insnTemplate << ", "; - fmt = "$"; + fmt = "$"; - switch ( op ) - { - case Op_mul: - // gas won't accept the two-operand form; skip to the source operand - i__ = 1; - // drop through - case Op_bound: - case Op_enter: - i = i__; - break; - default: - i = nOperands - 1 - i__; // operand = & operands[ nOperands - 1 - i ]; - break; - } - operand = & operands[ i ]; + switch (op) { + case Op_mul: + // gas won't accept the two-operand form; skip to the source operand + i__ = 1; + // drop through + case Op_bound: + case Op_enter: + i = i__; + break; + default: + i = nOperands - 1 - i__; // operand = & operands[ nOperands - 1 - i ]; + break; + } + operand = &operands[i]; - switch ( operand->cls ) - { - case Opr_Immediate: - // for implementing offset: - // $var + $7 // fails - // $var + 7 // ok - // $7 + $var // ok + switch (operand->cls) { + case Opr_Immediate: + // for implementing offset: + // $var + $7 // fails + // $var + 7 // ok + // $7 + $var // ok - // DMD doesn't seem to allow this - /* - if ( opTakesLabel() ) tho... (near ptr would be abs?) - fmt = "$a"; // GAS won't accept "jmp $42"; must be "jmp 42" (rel) or "jmp *42" (abs) - */ - if ( opTakesLabel() ) - { - // "relative addressing not allowed in branch instructions" .. - stmt->error ( "integer constant not allowed in branch instructions" ); - return false; - } + // DMD doesn't seem to allow this + /* + if ( opTakesLabel() ) tho... (near ptr would be abs?) + fmt = "$a"; // GAS won't accept "jmp $42"; must be "jmp 42" (rel) or + "jmp *42" (abs) + */ + if (opTakesLabel()) { + // "relative addressing not allowed in branch instructions" .. + stmt->error("integer constant not allowed in branch instructions"); + return false; + } - if ( operand->symbolDisplacement.dim && - isLocalSize ( ( Expression * ) operand->symbolDisplacement.data[0] ) ) - { - // handle __LOCAL_SIZE, which in this constant, is an immediate - // should do this in slotexp.. - addOperand ( "$", Arg_LocalSize, - ( Expression * ) operand->symbolDisplacement.data[0], asmcode ); - if ( operand->constDisplacement ) - insnTemplate << '+'; - else - break; - } + if (operand->symbolDisplacement.dim && + isLocalSize((Expression *)operand->symbolDisplacement.data[0])) { + // handle __LOCAL_SIZE, which in this constant, is an immediate + // should do this in slotexp.. + addOperand("$", Arg_LocalSize, + (Expression *)operand->symbolDisplacement.data[0], + asmcode); + if (operand->constDisplacement) + insnTemplate << '+'; + else + break; + } - if ( operand->symbolDisplacement.dim ) - { - fmt = "$a"; - addOperand ( "$", Arg_Pointer, - ( Expression * ) operand->symbolDisplacement.data[0], - asmcode ); + if (operand->symbolDisplacement.dim) { + fmt = "$a"; + addOperand("$", Arg_Pointer, + (Expression *)operand->symbolDisplacement.data[0], + asmcode); - if ( operand->constDisplacement ) - insnTemplate << '+'; - else - // skip the addOperand(fmt, Arg_Integer...) below - break; - } - addOperand ( fmt, Arg_Integer, newIntExp ( operand->constDisplacement ), asmcode ); - break; - case Opr_Reg: - if ( opInfo->operands[i] & Opr_Dest ) - { - Reg clbr_reg = ( Reg ) regInfo[operand->reg].baseReg; - if ( clbr_reg != Reg_Invalid ) - { - asmcode->regs[clbr_reg] = true; - } - } - if ( opTakesLabel() ) - insnTemplate << '*'; - writeReg ( operand->reg ); - /* - insnTemplate << "%"; - insnTemplate << regInfo[operand->reg].name; - */ - break; - case Opr_Mem: - // better: use output operands for simple variable references - if ( ( opInfo->operands[i] & Opr_Update ) == Opr_Update ) - { - mode = Mode_Update; - } - else if ( opInfo->operands[i] & Opr_Dest ) - { - mode = Mode_Output; - } - else - { - mode = Mode_Input; - } + if (operand->constDisplacement) + insnTemplate << '+'; + else + // skip the addOperand(fmt, Arg_Integer...) below + break; + } + addOperand(fmt, Arg_Integer, newIntExp(operand->constDisplacement), + asmcode); + break; + case Opr_Reg: + if (opInfo->operands[i] & Opr_Dest) { + Reg clbr_reg = (Reg)regInfo[operand->reg].baseReg; + if (clbr_reg != Reg_Invalid) { + asmcode->regs[clbr_reg] = true; + } + } + if (opTakesLabel()) + insnTemplate << '*'; + writeReg(operand->reg); + /* + insnTemplate << "%"; + insnTemplate << regInfo[operand->reg].name; + */ + break; + case Opr_Mem: + // better: use output operands for simple variable references + if ((opInfo->operands[i] & Opr_Update) == Opr_Update) { + mode = Mode_Update; + } else if (opInfo->operands[i] & Opr_Dest) { + mode = Mode_Output; + } else { + mode = Mode_Input; + } - use_star = opTakesLabel(); + use_star = opTakesLabel(); - if (Logger::enabled()) { - Logger::cout() << "Opr_Mem\n"; - LOG_SCOPE - Logger::cout() << "baseReg: " << operand->baseReg << '\n'; - Logger::cout() << "segmentPrefix: " << operand->segmentPrefix << '\n'; - Logger::cout() << "constDisplacement: " << operand->constDisplacement << '\n'; - for (unsigned i = 0; i < operand->symbolDisplacement.dim; i++) { - Expression* expr = static_cast(operand->symbolDisplacement.data[i]); - Logger::cout() << "symbolDisplacement[" << i << "] = " << expr->toChars() << '\n'; - } - } - if ( operand->segmentPrefix != Reg_Invalid ) - { - if (op != Op_Branch) - { - writeReg(operand->segmentPrefix); - insnTemplate << ':'; - } - else - stmt->error("Cannot generate a segment prefix for a branching instruction"); - } - if ( (operand->segmentPrefix != Reg_Invalid && operand->symbolDisplacement.dim == 0) - || operand->constDisplacement ) - { - insnTemplate << operand->constDisplacement; - if ( operand->symbolDisplacement.dim ) - { - insnTemplate << '+'; - } - operand->constDisplacement = 0; - //addOperand(fmt, Arg_Integer, newIntExp(operand->constDisplacement), asmcode); - if ( opInfo->operands[i] & Opr_Dest ) - asmcode->clobbersMemory = 1; - } - if ( operand->symbolDisplacement.dim ) - { - Expression * e = ( Expression * ) operand->symbolDisplacement.data[0]; - Declaration * decl = 0; + if (Logger::enabled()) { + Logger::cout() << "Opr_Mem\n"; + LOG_SCOPE + Logger::cout() << "baseReg: " << operand->baseReg << '\n'; + Logger::cout() << "segmentPrefix: " << operand->segmentPrefix << '\n'; + Logger::cout() << "constDisplacement: " << operand->constDisplacement + << '\n'; + for (unsigned i = 0; i < operand->symbolDisplacement.dim; i++) { + Expression *expr = + static_cast(operand->symbolDisplacement.data[i]); + Logger::cout() << "symbolDisplacement[" << i + << "] = " << expr->toChars() << '\n'; + } + } + if (operand->segmentPrefix != Reg_Invalid) { + if (op != Op_Branch) { + writeReg(operand->segmentPrefix); + insnTemplate << ':'; + } else + stmt->error( + "Cannot generate a segment prefix for a branching instruction"); + } + if ((operand->segmentPrefix != Reg_Invalid && + operand->symbolDisplacement.dim == 0) || + operand->constDisplacement) { + insnTemplate << operand->constDisplacement; + if (operand->symbolDisplacement.dim) { + insnTemplate << '+'; + } + operand->constDisplacement = 0; + // addOperand(fmt, Arg_Integer, newIntExp(operand->constDisplacement), + // asmcode); + if (opInfo->operands[i] & Opr_Dest) + asmcode->clobbersMemory = 1; + } + if (operand->symbolDisplacement.dim) { + Expression *e = (Expression *)operand->symbolDisplacement.data[0]; + Declaration *decl = 0; - if ( e->op == TOKvar ) - decl = ( ( VarExp * ) e )->var; + if (e->op == TOKvar) + decl = ((VarExp *)e)->var; - if ( operand->baseReg != Reg_Invalid && - decl && ! decl->isDataseg() ) - { + if (operand->baseReg != Reg_Invalid && decl && !decl->isDataseg()) { - // Use the offset from frame pointer + // Use the offset from frame pointer - /* GCC doesn't give the front end access to stack offsets - when optimization is turned on (3.x) or at all (4.x). + /* GCC doesn't give the front end access to stack offsets + when optimization is turned on (3.x) or at all (4.x). - Try to convert var[EBP] (or var[ESP] for naked funcs) to - a memory expression that does not require us to know - the stack offset. - */ + Try to convert var[EBP] (or var[ESP] for naked funcs) to + a memory expression that does not require us to know + the stack offset. + */ - if ( operand->indexReg == Reg_Invalid && - decl->isVarDeclaration() && + if (operand->indexReg == Reg_Invalid && decl->isVarDeclaration() && #ifndef ASM_X86_64 - ( ( operand->baseReg == Reg_EBP && ! sc->func->naked ) || - ( operand->baseReg == Reg_ESP && sc->func->naked ) ) ) + ((operand->baseReg == Reg_EBP && !sc->func->naked) || + (operand->baseReg == Reg_ESP && sc->func->naked))) #else - ( ( ( operand->baseReg == Reg_EBP || operand->baseReg == Reg_RBP ) && ! sc->func->naked ) || - ( ( operand->baseReg == Reg_ESP || operand->baseReg == Reg_RSP ) && sc->func->naked ) ) ) + (((operand->baseReg == Reg_EBP || + operand->baseReg == Reg_RBP) && + !sc->func->naked) || + ((operand->baseReg == Reg_ESP || + operand->baseReg == Reg_RSP) && + sc->func->naked))) #endif - { + { - e = new AddrExp ( Loc(), e ); - e->type = decl->type->pointerTo(); + e = new AddrExp(Loc(), e); + e->type = decl->type->pointerTo(); - operand->constDisplacement = 0; - operand->baseReg = Reg_Invalid; + operand->constDisplacement = 0; + operand->baseReg = Reg_Invalid; - addOperand ( fmt, Arg_Memory, e, asmcode, mode ); + addOperand(fmt, Arg_Memory, e, asmcode, mode); - } - else - { - // FIXME: what is this ? - addOperand2 ( "${",":a}", Arg_FrameRelative, e, asmcode ); - } - if ( opInfo->operands[i] & Opr_Dest ) - asmcode->clobbersMemory = 1; - } - else - { - // Plain memory reference to variable or reference to label. - - /* If in a reg, DMD moves to memory.. even with -O, so we'll do the same - by always using the "m" contraint. - - In order to get the correct output for function and label symbols, - the %an format must be used with the "p" constraint. - */ - if ( isDollar ( e ) ) - { - stmt->error("dollar labels are not supported"); - asmcode->dollarLabel = 1; - } - else if ( e->op == TOKdsymbol ) - { - LabelDsymbol * lbl = ( LabelDsymbol * ) ( ( DsymbolExp * ) e )->s; - stmt->isBranchToLabel = lbl; - - use_star = false; - addLabel ( lbl->ident->toChars() ); - } - else if ( ( decl && decl->isCodeseg() ) ) // if function or label - { - use_star = false; - // simply write out the mangle - if ( prependExtraUnderscore() ) - { - insnTemplate << "_"; - } - insnTemplate << mangle(decl); -// addOperand2("${", ":c}", Arg_Pointer, e, asmcode); - } - else - { - if ( use_star ) - { - insnTemplate << '*'; - use_star = false; - } - - if ( !sc->func->naked ) // no addrexp in naked asm please :) - { - Type* tt = e->type->pointerTo(); - e = new AddrExp ( Loc(), e ); - e->type = tt; - } - - addOperand ( fmt, Arg_Memory, e, asmcode, mode ); - } - } - } - if ( use_star ) - insnTemplate << '*'; - if ( operand->baseReg != Reg_Invalid || operand->indexReg != Reg_Invalid ) - { - insnTemplate << '('; - if ( operand->baseReg != Reg_Invalid ) - writeReg ( operand->baseReg ); - if ( operand->indexReg != Reg_Invalid ) - { - insnTemplate << ','; - writeReg ( operand->indexReg ); - if ( operand->scale ) - { - insnTemplate << "," << operand->scale; - } - } - insnTemplate << ')'; - if ( opInfo->operands[i] & Opr_Dest ) - asmcode->clobbersMemory = 1; - } - break; - case Opr_Invalid: - return false; - } + } else { + // FIXME: what is this ? + addOperand2("${", ":a}", Arg_FrameRelative, e, asmcode); } + if (opInfo->operands[i] & Opr_Dest) + asmcode->clobbersMemory = 1; + } else { + // Plain memory reference to variable or reference to label. - asmcode->insnTemplate = insnTemplate.str(); - Logger::cout() << "insnTemplate = " << asmcode->insnTemplate << '\n'; - return true; - } + /* If in a reg, DMD moves to memory.. even with -O, so we'll do the + same + by always using the "m" contraint. - bool isIntExp ( Expression * exp ) - { - return exp->op == TOKint64; - } - bool isRegExp ( Expression * exp ) { return exp->op == TOKmod; } // ewww.%% - bool isLocalSize ( Expression * exp ) - { - // cleanup: make a static var - return exp->op == TOKidentifier && ( ( IdentifierExp * ) exp )->ident == Id::__LOCAL_SIZE; - } - bool isDollar ( Expression * exp ) - { - return exp->op == TOKidentifier && ( ( IdentifierExp * ) exp )->ident == Id::dollar; - } + In order to get the correct output for function and label + symbols, + the %an format must be used with the "p" constraint. + */ + if (isDollar(e)) { + stmt->error("dollar labels are not supported"); + asmcode->dollarLabel = 1; + } else if (e->op == TOKdsymbol) { + LabelDsymbol *lbl = (LabelDsymbol *)((DsymbolExp *)e)->s; + stmt->isBranchToLabel = lbl; - Expression * newRegExp ( int regno ) - { - IntegerExp * e = new IntegerExp ( regno ); - e->op = TOKmod; - return e; + use_star = false; + addLabel(lbl->ident->toChars()); + } else if ((decl && decl->isCodeseg())) // if function or label + { + use_star = false; + // simply write out the mangle + if (prependExtraUnderscore()) { + insnTemplate << "_"; + } + insnTemplate << mangle(decl); + // addOperand2("${", ":c}", Arg_Pointer, e, asmcode); + } else { + if (use_star) { + insnTemplate << '*'; + use_star = false; + } + + if (!sc->func->naked) // no addrexp in naked asm please :) + { + Type *tt = e->type->pointerTo(); + e = new AddrExp(Loc(), e); + e->type = tt; + } + + addOperand(fmt, Arg_Memory, e, asmcode, mode); + } + } } + if (use_star) + insnTemplate << '*'; + if (operand->baseReg != Reg_Invalid || + operand->indexReg != Reg_Invalid) { + insnTemplate << '('; + if (operand->baseReg != Reg_Invalid) + writeReg(operand->baseReg); + if (operand->indexReg != Reg_Invalid) { + insnTemplate << ','; + writeReg(operand->indexReg); + if (operand->scale) { + insnTemplate << "," << operand->scale; + } + } + insnTemplate << ')'; + if (opInfo->operands[i] & Opr_Dest) + asmcode->clobbersMemory = 1; + } + break; + case Opr_Invalid: + return false; + } + } + + asmcode->insnTemplate = insnTemplate.str(); + Logger::cout() << "insnTemplate = " << asmcode->insnTemplate << '\n'; + return true; + } + + bool isIntExp(Expression *exp) { return exp->op == TOKint64; } + bool isRegExp(Expression *exp) { return exp->op == TOKmod; } // ewww.%% + bool isLocalSize(Expression *exp) { + // cleanup: make a static var + return exp->op == TOKidentifier && + ((IdentifierExp *)exp)->ident == Id::__LOCAL_SIZE; + } + bool isDollar(Expression *exp) { + return exp->op == TOKidentifier && + ((IdentifierExp *)exp)->ident == Id::dollar; + } + + Expression *newRegExp(int regno) { + IntegerExp *e = new IntegerExp(regno); + e->op = TOKmod; + return e; + } #ifndef ASM_X86_64 - Expression * newIntExp ( int v /* %% type */ ) - { - // Only handles 32-bit numbers as this is IA-32. - return new IntegerExp ( stmt->loc, v, Type::tint32 ); - } + Expression *newIntExp(int v /* %% type */) { + // Only handles 32-bit numbers as this is IA-32. + return new IntegerExp(stmt->loc, v, Type::tint32); + } #else - Expression * newIntExp ( long long v /* %% type */ ) - { - // Handle 64 bit - return new IntegerExp ( stmt->loc, v, Type::tint64 ); - } + Expression *newIntExp(long long v /* %% type */) { + // Handle 64 bit + return new IntegerExp(stmt->loc, v, Type::tint64); + } #endif - void slotExp ( Expression * exp ) - { - /* - if offset, make a note + void slotExp(Expression *exp) { + /* + if offset, make a note - if integer, add to immediate - if reg: - if not in bracket, set reg (else error) - if in bracket: - if not base, set base - if not index, set index - else, error - if symbol: - set symbol field - */ + if integer, add to immediate + if reg: + if not in bracket, set reg (else error) + if in bracket: + if not base, set base + if not index, set index + else, error + if symbol: + set symbol field + */ - bool is_offset = false; - if ( exp->op == TOKaddress ) - { - exp = ( ( AddrExp * ) exp )->e1; - is_offset = true; - } + bool is_offset = false; + if (exp->op == TOKaddress) { + exp = ((AddrExp *)exp)->e1; + is_offset = true; + } - if ( isIntExp ( exp ) ) - { - if ( is_offset ) - invalidExpression(); - operand->constDisplacement += exp->toInteger(); - if ( ! operand->inBracket ) - operand->hasNumber = 1; - } - else if ( isRegExp ( exp ) ) - { - if ( is_offset ) - invalidExpression(); - if ( ! operand->inBracket ) - { - if ( operand->reg == Reg_Invalid ) - operand->reg = ( Reg ) exp->toInteger(); - else - stmt->error ( "too many registers in operand (use brackets)" ); - } - else - { - if ( operand->baseReg == Reg_Invalid ) - operand->baseReg = ( Reg ) exp->toInteger(); - else if ( operand->indexReg == Reg_Invalid ) - { - operand->indexReg = ( Reg ) exp->toInteger(); - operand->scale = 1; - } - else - { - stmt->error ( "too many registers memory operand" ); - } - } - } - else if ( exp->op == TOKvar ) - { - VarDeclaration * v = ( ( VarExp * ) exp )->var->isVarDeclaration(); + if (isIntExp(exp)) { + if (is_offset) + invalidExpression(); + operand->constDisplacement += exp->toInteger(); + if (!operand->inBracket) + operand->hasNumber = 1; + } else if (isRegExp(exp)) { + if (is_offset) + invalidExpression(); + if (!operand->inBracket) { + if (operand->reg == Reg_Invalid) + operand->reg = (Reg)exp->toInteger(); + else + stmt->error("too many registers in operand (use brackets)"); + } else { + if (operand->baseReg == Reg_Invalid) + operand->baseReg = (Reg)exp->toInteger(); + else if (operand->indexReg == Reg_Invalid) { + operand->indexReg = (Reg)exp->toInteger(); + operand->scale = 1; + } else { + stmt->error("too many registers memory operand"); + } + } + } else if (exp->op == TOKvar) { + VarDeclaration *v = ((VarExp *)exp)->var->isVarDeclaration(); - if ( v && v->storage_class & STCfield ) - { - operand->constDisplacement += v->offset; - if ( ! operand->inBracket ) - operand->hasNumber = 1; - } - else - { - if ( v && v->type->isscalar() ) - { - // DMD doesn't check Tcomplex*, and counts Tcomplex32 as Tfloat64 - TY ty = v->type->toBasetype()->ty; - operand->dataSizeHint = ty == Tfloat80 || ty == Timaginary80 ? - Extended_Ptr : ( PtrType ) v->type->size ( Loc() ); - } - - if ( ! operand->symbolDisplacement.dim ) - { - if ( is_offset && ! operand->inBracket ) - operand->isOffset = 1; - operand->symbolDisplacement.push ( exp ); - } - else - { - stmt->error ( "too many symbols in operand" ); - } - } - } - else if ( exp->op == TOKidentifier || exp->op == TOKdsymbol ) - { - // %% localsize could be treated as a simple constant.. - // change to addSymbolDisp(e) - if ( ! operand->symbolDisplacement.dim ) - { - operand->symbolDisplacement.push ( exp ); - } - else - { - stmt->error ( "too many symbols in operand" ); - } - } - else if ( exp == Handled ) - { - // nothing - } - else - { - stmt->error ( "invalid operand" ); - } + if (v && v->storage_class & STCfield) { + operand->constDisplacement += v->offset; + if (!operand->inBracket) + operand->hasNumber = 1; + } else { + if (v && v->type->isscalar()) { + // DMD doesn't check Tcomplex*, and counts Tcomplex32 as Tfloat64 + TY ty = v->type->toBasetype()->ty; + operand->dataSizeHint = ty == Tfloat80 || ty == Timaginary80 + ? Extended_Ptr + : (PtrType)v->type->size(Loc()); } - void invalidExpression() - { - // %% report operand number - stmt->error ( "invalid expression" ); + if (!operand->symbolDisplacement.dim) { + if (is_offset && !operand->inBracket) + operand->isOffset = 1; + operand->symbolDisplacement.push(exp); + } else { + stmt->error("too many symbols in operand"); } + } + } else if (exp->op == TOKidentifier || exp->op == TOKdsymbol) { + // %% localsize could be treated as a simple constant.. + // change to addSymbolDisp(e) + if (!operand->symbolDisplacement.dim) { + operand->symbolDisplacement.push(exp); + } else { + stmt->error("too many symbols in operand"); + } + } else if (exp == Handled) { + // nothing + } else { + stmt->error("invalid operand"); + } + } - Expression * intOp ( TOK op, Expression * e1, Expression * e2 ) - { - Expression * e; - if ( isIntExp ( e1 ) && ( ! e2 || isIntExp ( e2 ) ) ) - { - switch ( op ) - { - case TOKadd: - if ( e2 ) - e = new AddExp ( stmt->loc, e1, e2 ); - else - e = e1; - break; - case TOKmin: - if ( e2 ) - e = new MinExp ( stmt->loc, e1, e2 ); - else - e = new NegExp ( stmt->loc, e1 ); - break; - case TOKmul: - e = new MulExp ( stmt->loc, e1, e2 ); - break; - case TOKdiv: - e = new DivExp ( stmt->loc, e1, e2 ); - break; - case TOKmod: - e = new ModExp ( stmt->loc, e1, e2 ); - break; - case TOKshl: - e = new ShlExp ( stmt->loc, e1, e2 ); - break; - case TOKshr: - e = new ShrExp ( stmt->loc, e1, e2 ); - break; - case TOKushr: - e = new UshrExp ( stmt->loc, e1, e2 ); - break; - case TOKnot: - e = new NotExp ( stmt->loc, e1 ); - break; - case TOKtilde: - e = new ComExp ( stmt->loc, e1 ); - break; - case TOKoror: - e = new OrOrExp(stmt->loc, e1, e2); - break; - case TOKandand: - e = new AndAndExp(stmt->loc, e1, e2); - break; - case TOKor: - e = new OrExp(stmt->loc, e1, e2); - break; - case TOKand: - e = new AndExp(stmt->loc, e1, e2); - break; - case TOKxor: - e = new XorExp(stmt->loc, e1, e2); - break; - case TOKequal: - case TOKnotequal: - e = new EqualExp(op, stmt->loc, e1, e2); - break; - case TOKgt: - case TOKge: - case TOKlt: - case TOKle: - e = new CmpExp(op, stmt->loc, e1, e2); - break; - default: - llvm_unreachable("Unknown integer operation."); - } - e = e->semantic ( sc ); - return e->ctfeInterpret(); - } - else - { - stmt->error ( "expected integer operand(s) for '%s'", Token::tochars[op] ); - return newIntExp ( 0 ); - } + void invalidExpression() { + // %% report operand number + stmt->error("invalid expression"); + } + + Expression *intOp(TOK op, Expression *e1, Expression *e2) { + Expression *e; + if (isIntExp(e1) && (!e2 || isIntExp(e2))) { + switch (op) { + case TOKadd: + if (e2) + e = new AddExp(stmt->loc, e1, e2); + else + e = e1; + break; + case TOKmin: + if (e2) + e = new MinExp(stmt->loc, e1, e2); + else + e = new NegExp(stmt->loc, e1); + break; + case TOKmul: + e = new MulExp(stmt->loc, e1, e2); + break; + case TOKdiv: + e = new DivExp(stmt->loc, e1, e2); + break; + case TOKmod: + e = new ModExp(stmt->loc, e1, e2); + break; + case TOKshl: + e = new ShlExp(stmt->loc, e1, e2); + break; + case TOKshr: + e = new ShrExp(stmt->loc, e1, e2); + break; + case TOKushr: + e = new UshrExp(stmt->loc, e1, e2); + break; + case TOKnot: + e = new NotExp(stmt->loc, e1); + break; + case TOKtilde: + e = new ComExp(stmt->loc, e1); + break; + case TOKoror: + e = new OrOrExp(stmt->loc, e1, e2); + break; + case TOKandand: + e = new AndAndExp(stmt->loc, e1, e2); + break; + case TOKor: + e = new OrExp(stmt->loc, e1, e2); + break; + case TOKand: + e = new AndExp(stmt->loc, e1, e2); + break; + case TOKxor: + e = new XorExp(stmt->loc, e1, e2); + break; + case TOKequal: + case TOKnotequal: + e = new EqualExp(op, stmt->loc, e1, e2); + break; + case TOKgt: + case TOKge: + case TOKlt: + case TOKle: + e = new CmpExp(op, stmt->loc, e1, e2); + break; + default: + llvm_unreachable("Unknown integer operation."); + } + e = e->semantic(sc); + return e->ctfeInterpret(); + } else { + stmt->error("expected integer operand(s) for '%s'", Token::tochars[op]); + return newIntExp(0); + } + } + + void parseOperand() { + Expression *exp = parseAsmExp(); + slotExp(exp); + if (isRegExp(exp)) + operand->dataSize = (PtrType)regInfo[exp->toInteger()].size; + } + + Expression *parseAsmExp() { return parseCondExp(); } + + Expression *parseCondExp() { + Expression *exp = parseLogOrExp(); + if (token->value == TOKquestion) { + nextToken(); + Expression *exp2 = parseCondExp(); + if (token->value != TOKcolon) + return exp; + nextToken(); + Expression *exp3 = parseCondExp(); + exp = exp->toUInteger() ? exp2 : exp3; + } + return exp; + } + + Expression *parseLogOrExp() { + Expression *exp = parseLogAndExp(); + while (token->value == TOKoror) { + nextToken(); + Expression *exp2 = parseLogAndExp(); + if (isIntExp(exp) && isIntExp(exp2)) + exp = intOp(TOKandand, exp, exp2); + else + stmt->error("bad integral operand"); + } + return exp; + } + + Expression *parseLogAndExp() { + Expression *exp = parseIncOrExp(); + while (token->value == TOKoror) { + nextToken(); + Expression *exp2 = parseIncOrExp(); + if (isIntExp(exp) && isIntExp(exp2)) + exp = intOp(TOKoror, exp, exp2); + else + stmt->error("bad integral operand"); + } + return exp; + } + + Expression *parseIncOrExp() { + Expression *exp = parseXOrExp(); + while (token->value == TOKor) { + nextToken(); + Expression *exp2 = parseXOrExp(); + if (isIntExp(exp) && isIntExp(exp2)) + exp = intOp(TOKor, exp, exp2); + else + stmt->error("bad integral operand"); + } + return exp; + } + + Expression *parseXOrExp() { + Expression *exp = parseAndExp(); + while (token->value == TOKxor) { + nextToken(); + Expression *exp2 = parseAndExp(); + if (isIntExp(exp) && isIntExp(exp2)) + exp = intOp(TOKxor, exp, exp2); + else + stmt->error("bad integral operand"); + } + return exp; + } + + Expression *parseAndExp() { + Expression *exp = parseEqualExp(); + while (token->value == TOKand) { + nextToken(); + Expression *exp2 = parseEqualExp(); + if (isIntExp(exp) && isIntExp(exp2)) + exp = intOp(TOKand, exp, exp2); + else + stmt->error("bad integral operand"); + } + return exp; + } + + Expression *parseEqualExp() { + Expression *exp = parseRelExp(); + while (1) { + switch (token->value) { + case TOKequal: + case TOKnotequal: { + TOK tok = token->value; + nextToken(); + Expression *exp2 = parseRelExp(); + if (isIntExp(exp) && isIntExp(exp2)) + exp = intOp(tok, exp, exp2); + else + stmt->error("bad integral operand"); + } + default: + return exp; + } + } + return exp; + } + + Expression *parseRelExp() { + Expression *exp = parseShiftExp(); + while (1) { + switch (token->value) { + case TOKgt: + case TOKge: + case TOKlt: + case TOKle: { + TOK tok = token->value; + nextToken(); + Expression *exp2 = parseShiftExp(); + if (isIntExp(exp) && isIntExp(exp2)) + exp = intOp(tok, exp, exp2); + else + stmt->error("bad integral operand"); + } + default: + return exp; + } + } + return exp; + } + + Expression *parseShiftExp() { + Expression *e1 = parseAddExp(); + Expression *e2; + + while (1) { + TOK tv = token->value; + switch (tv) { + case TOKshl: + case TOKshr: + case TOKushr: + nextToken(); + e2 = parseAddExp(); + e1 = intOp(tv, e1, e2); + continue; + default: + break; + } + break; + } + return e1; + } + + Expression *parseAddExp() { + Expression *e1 = parseMultExp(); + Expression *e2; + + while (1) { + TOK tv = token->value; + switch (tv) { + case TOKadd: + nextToken(); + e2 = parseMultExp(); + if (isIntExp(e1) && isIntExp(e2)) + e1 = intOp(tv, e1, e2); + else { + slotExp(e1); + slotExp(e2); + e1 = Handled; } - - void parseOperand() - { - Expression * exp = parseAsmExp(); - slotExp ( exp ); - if ( isRegExp ( exp ) ) - operand->dataSize = ( PtrType ) regInfo[exp->toInteger() ].size; + continue; + case TOKmin: + // Note: no support for symbol address difference + nextToken(); + e2 = parseMultExp(); + if (isIntExp(e1) && isIntExp(e2)) + e1 = intOp(tv, e1, e2); + else { + slotExp(e1); + e2 = intOp(TOKmin, e2, NULL); // verifies e2 is an int + slotExp(e2); + e1 = Handled; } + continue; + default: + break; + } + break; + } + return e1; + } - Expression * parseAsmExp() - { - return parseCondExp(); - } + bool tryScale(Expression *e1, Expression *e2) { + Expression *et; + if (isIntExp(e1) && isRegExp(e2)) { + et = e1; + e1 = e2; + e2 = et; + goto do_scale; + } else if (isRegExp(e1) && isIntExp(e2)) { + do_scale: + if (!operand->inBracket) { + invalidExpression(); // maybe should allow, e.g. DS:EBX+EAX*4 + } - Expression * parseCondExp() - { - Expression * exp = parseLogOrExp(); - if (token->value == TOKquestion) - { - nextToken(); - Expression * exp2 = parseCondExp(); - if (token->value != TOKcolon) - return exp; - nextToken(); - Expression * exp3 = parseCondExp(); - exp = exp->toUInteger() ? exp2 : exp3; - } - return exp; - } + if (operand->scale || operand->indexReg != Reg_Invalid) { + invalidExpression(); + return true; + } - Expression * parseLogOrExp() - { - Expression * exp = parseLogAndExp(); - while (token->value == TOKoror) - { - nextToken(); - Expression * exp2 = parseLogAndExp(); - if (isIntExp(exp) && isIntExp(exp2)) - exp = intOp(TOKandand, exp, exp2); - else - stmt->error("bad integral operand"); - } - return exp; - } + operand->indexReg = (Reg)e1->toInteger(); + operand->scale = e2->toInteger(); + switch (operand->scale) { + case 1: + case 2: + case 4: + case 8: + // ok; do nothing + break; + default: + stmt->error("invalid index register scale '%d'", operand->scale); + return true; + } - Expression * parseLogAndExp() - { - Expression * exp = parseIncOrExp(); - while (token->value == TOKoror) - { - nextToken(); - Expression * exp2 = parseIncOrExp(); - if (isIntExp(exp) && isIntExp(exp2)) - exp = intOp(TOKoror, exp, exp2); - else - stmt->error("bad integral operand"); - } - return exp; - } + return true; + } + return false; + } - Expression * parseIncOrExp() - { - Expression * exp = parseXOrExp(); - while (token->value == TOKor) - { - nextToken(); - Expression * exp2 = parseXOrExp(); - if (isIntExp(exp) && isIntExp(exp2)) - exp = intOp(TOKor, exp, exp2); - else - stmt->error("bad integral operand"); - } - return exp; - } + Expression *parseMultExp() { + Expression *e1 = parseBrExp(); + Expression *e2; - Expression * parseXOrExp() - { - Expression * exp = parseAndExp(); - while (token->value == TOKxor) - { - nextToken(); - Expression * exp2 = parseAndExp(); - if (isIntExp(exp) && isIntExp(exp2)) - exp = intOp(TOKxor, exp, exp2); - else - stmt->error("bad integral operand"); - } - return exp; - } + while (1) { + TOK tv = token->value; + switch (tv) { + case TOKmul: + nextToken(); + e2 = parseMultExp(); + if (isIntExp(e1) && isIntExp(e2)) + e1 = intOp(tv, e1, e2); + else if (tryScale(e1, e2)) + e1 = Handled; + else + invalidExpression(); + continue; + case TOKdiv: + case TOKmod: + nextToken(); + e2 = parseMultExp(); + e1 = intOp(tv, e1, e2); + continue; + default: + break; + } + break; + } + return e1; + } - Expression * parseAndExp() - { - Expression * exp = parseEqualExp(); - while (token->value == TOKand) - { - nextToken(); - Expression * exp2 = parseEqualExp(); - if (isIntExp(exp) && isIntExp(exp2)) - exp = intOp(TOKand, exp, exp2); - else - stmt->error("bad integral operand"); - } - return exp; - } + Expression *parseBrExp() { + // %% check (why is bracket lower precends..) + // 3+4[eax] -> 3 + (4 [EAX]) .. - Expression * parseEqualExp() - { - Expression * exp = parseRelExp(); - while (1) - { - switch (token->value) - { - case TOKequal: - case TOKnotequal: - { - TOK tok = token->value; - nextToken(); - Expression * exp2 = parseRelExp(); - if (isIntExp(exp) && isIntExp(exp2)) - exp = intOp(tok, exp, exp2); - else - stmt->error("bad integral operand"); - } - default: - return exp; - } - } - return exp; - } + // only one bracked allowed, so this doesn't quite handle + // the spec'd syntax + Expression *e; - Expression * parseRelExp() - { - Expression * exp = parseShiftExp(); - while (1) - { - switch (token->value) - { - case TOKgt: - case TOKge: - case TOKlt: - case TOKle: - { - TOK tok = token->value; - nextToken(); - Expression * exp2 = parseShiftExp(); - if (isIntExp(exp) && isIntExp(exp2)) - exp = intOp(tok, exp, exp2); - else - stmt->error("bad integral operand"); - } - default: - return exp; - } - } - return exp; - } + if (token->value == TOKlbracket) + e = Handled; + else + e = parseUnaExp(); - Expression * parseShiftExp() - { - Expression * e1 = parseAddExp(); - Expression * e2; + // DMD allows multiple bracket expressions. + while (token->value == TOKlbracket) { + nextToken(); - while ( 1 ) - { - TOK tv = token->value; - switch ( tv ) - { - case TOKshl: - case TOKshr: - case TOKushr: - nextToken(); - e2 = parseAddExp(); - e1 = intOp ( tv, e1, e2 ); - continue; - default: - break; - } - break; - } - return e1; - } + operand->inBracket = operand->hasBracket = 1; + slotExp(parseAsmExp()); + operand->inBracket = 0; - Expression * parseAddExp() - { - Expression * e1 = parseMultExp(); - Expression * e2; + if (token->value == TOKrbracket) + nextToken(); + else + stmt->error("missing ']'"); + } - while ( 1 ) - { - TOK tv = token->value; - switch ( tv ) - { - case TOKadd: - nextToken(); - e2 = parseMultExp(); - if ( isIntExp ( e1 ) && isIntExp ( e2 ) ) - e1 = intOp ( tv, e1, e2 ); - else - { - slotExp ( e1 ); - slotExp ( e2 ); - e1 = Handled; - } - continue; - case TOKmin: - // Note: no support for symbol address difference - nextToken(); - e2 = parseMultExp(); - if ( isIntExp ( e1 ) && isIntExp ( e2 ) ) - e1 = intOp ( tv, e1, e2 ); - else - { - slotExp ( e1 ); - e2 = intOp ( TOKmin, e2, NULL ); // verifies e2 is an int - slotExp ( e2 ); - e1 = Handled; - } - continue; - default: - break; - } - break; - } - return e1; - } + return e; + } - bool tryScale ( Expression * e1, Expression * e2 ) - { - Expression * et; - if ( isIntExp ( e1 ) && isRegExp ( e2 ) ) - { - et = e1; - e1 = e2; - e2 = et; - goto do_scale; - } - else if ( isRegExp ( e1 ) && isIntExp ( e2 ) ) - { - do_scale: - if ( ! operand->inBracket ) - { - invalidExpression(); // maybe should allow, e.g. DS:EBX+EAX*4 - } - - if ( operand->scale || operand->indexReg != Reg_Invalid ) - { - invalidExpression(); - return true; - } - - operand->indexReg = ( Reg ) e1->toInteger(); - operand->scale = e2->toInteger(); - switch ( operand->scale ) - { - case 1: - case 2: - case 4: - case 8: - // ok; do nothing - break; - default: - stmt->error ( "invalid index register scale '%d'", operand->scale ); - return true; - } - - return true; - } - return false; - } - - Expression * parseMultExp() - { - Expression * e1 = parseBrExp(); - Expression * e2; - - while ( 1 ) - { - TOK tv = token->value; - switch ( tv ) - { - case TOKmul: - nextToken(); - e2 = parseMultExp(); - if ( isIntExp ( e1 ) && isIntExp ( e2 ) ) - e1 = intOp ( tv, e1, e2 ); - else if ( tryScale ( e1,e2 ) ) - e1 = Handled; - else - invalidExpression(); - continue; - case TOKdiv: - case TOKmod: - nextToken(); - e2 = parseMultExp(); - e1 = intOp ( tv, e1, e2 ); - continue; - default: - break; - } - break; - } - return e1; - } - - Expression * parseBrExp() - { - // %% check (why is bracket lower precends..) - // 3+4[eax] -> 3 + (4 [EAX]) .. - - // only one bracked allowed, so this doesn't quite handle - // the spec'd syntax - Expression * e; - - if ( token->value == TOKlbracket ) - e = Handled; - else - e = parseUnaExp(); - - // DMD allows multiple bracket expressions. - while ( token->value == TOKlbracket ) - { - nextToken(); - - operand->inBracket = operand->hasBracket = 1; - slotExp ( parseAsmExp() ); - operand->inBracket = 0; - - if ( token->value == TOKrbracket ) - nextToken(); - else - stmt->error ( "missing ']'" ); - } - - return e; - } - - PtrType isPtrType ( Token * tok ) - { - switch ( tok->value ) - { - case TOKint8: return Byte_Ptr; - case TOKint16: return Short_Ptr; - case TOKint32: return Int_Ptr; + PtrType isPtrType(Token *tok) { + switch (tok->value) { + case TOKint8: + return Byte_Ptr; + case TOKint16: + return Short_Ptr; + case TOKint32: + return Int_Ptr; #ifndef ASM_X86_64 - // 'long ptr' isn't accepted? +// 'long ptr' isn't accepted? #else - case TOKint64: return QWord_Ptr; + case TOKint64: + return QWord_Ptr; #endif - case TOKfloat32: return Float_Ptr; - case TOKfloat64: return Double_Ptr; - case TOKfloat80: return Extended_Ptr; - case TOKidentifier: - for ( int i = 0; i < N_PtrNames; i++ ) - if ( tok->ident == ptrTypeIdentTable[i] ) - return ptrTypeValueTable[i]; - break; - default: - break; - } - return Default_Ptr; - } + case TOKfloat32: + return Float_Ptr; + case TOKfloat64: + return Double_Ptr; + case TOKfloat80: + return Extended_Ptr; + case TOKidentifier: + for (int i = 0; i < N_PtrNames; i++) + if (tok->ident == ptrTypeIdentTable[i]) + return ptrTypeValueTable[i]; + break; + default: + break; + } + return Default_Ptr; + } - Expression * parseUnaExp() - { - Expression * e = NULL; - PtrType ptr_type; + Expression *parseUnaExp() { + Expression *e = NULL; + PtrType ptr_type; - // First, check for type prefix. - if ( token->value != TOKeof && - peekToken()->value == TOKidentifier && - peekToken()->ident == Id::ptr ) - { + // First, check for type prefix. + if (token->value != TOKeof && peekToken()->value == TOKidentifier && + peekToken()->ident == Id::ptr) { - ptr_type = isPtrType ( token ); - if ( ptr_type != Default_Ptr ) - { - if ( operand->dataSize == Default_Ptr ) - operand->dataSize = ptr_type; - else - stmt->error ( "multiple specifications of operand size" ); - } - else - stmt->error ( "unknown operand size '%s'", token->toChars() ); - nextToken(); - nextToken(); - return parseAsmExp(); - } + ptr_type = isPtrType(token); + if (ptr_type != Default_Ptr) { + if (operand->dataSize == Default_Ptr) + operand->dataSize = ptr_type; + else + stmt->error("multiple specifications of operand size"); + } else + stmt->error("unknown operand size '%s'", token->toChars()); + nextToken(); + nextToken(); + return parseAsmExp(); + } - TOK tv = token->value; - switch ( tv ) - { - case TOKidentifier: - if ( token->ident == ident_seg ) - { - nextToken(); - stmt->error ( "'seg' not supported" ); - e = parseAsmExp(); - } - else if ( token->ident == Id::offset || - token->ident == Id::offsetof ) - { - if ( token->ident == Id::offset && ! global.params.useDeprecated ) - stmt->error ( "offset deprecated, use offsetof" ); - nextToken(); - e = parseAsmExp(); - e = new AddrExp ( stmt->loc, e ); - } - else - { - // primary exp - break; - } - return e; - case TOKadd: - case TOKmin: - case TOKnot: - case TOKtilde: - nextToken(); - e = parseUnaExp(); - return intOp ( tv, e, NULL ); - default: - // primary exp - break; - } - return parsePrimaryExp(); - } + TOK tv = token->value; + switch (tv) { + case TOKidentifier: + if (token->ident == ident_seg) { + nextToken(); + stmt->error("'seg' not supported"); + e = parseAsmExp(); + } else if (token->ident == Id::offset || token->ident == Id::offsetof) { + if (token->ident == Id::offset && !global.params.useDeprecated) + stmt->error("offset deprecated, use offsetof"); + nextToken(); + e = parseAsmExp(); + e = new AddrExp(stmt->loc, e); + } else { + // primary exp + break; + } + return e; + case TOKadd: + case TOKmin: + case TOKnot: + case TOKtilde: + nextToken(); + e = parseUnaExp(); + return intOp(tv, e, NULL); + default: + // primary exp + break; + } + return parsePrimaryExp(); + } - Expression * parsePrimaryExp() - { - Expression * e; - Identifier * ident = NULL; + Expression *parsePrimaryExp() { + Expression *e; + Identifier *ident = NULL; - // get rid of short/long prefixes for branches - if (opTakesLabel() && (token->value == TOKint16 || token->value == TOKint64)) - nextToken(); + // get rid of short/long prefixes for branches + if (opTakesLabel() && + (token->value == TOKint16 || token->value == TOKint64)) + nextToken(); - switch ( token->value ) - { - case TOKint32v: - case TOKuns32v: - case TOKint64v: - case TOKuns64v: - // semantic here? + switch (token->value) { + case TOKint32v: + case TOKuns32v: + case TOKint64v: + case TOKuns64v: +// semantic here? #ifndef ASM_X86_64 - // %% for tok64 really should use 64bit type - e = new IntegerExp ( stmt->loc, token->uns64value, Type::tint32 ); + // %% for tok64 really should use 64bit type + e = new IntegerExp(stmt->loc, token->uns64value, Type::tint32); #else - e = new IntegerExp ( stmt->loc, token->uns64value, Type::tint64 ); + e = new IntegerExp(stmt->loc, token->uns64value, Type::tint64); #endif - nextToken(); - break; - case TOKfloat32v: - case TOKfloat64v: - case TOKfloat80v: - // %% need different types? - e = new RealExp ( stmt->loc, token->float80value, Type::tfloat80 ); - nextToken(); - break; - case TOKidentifier: - { - ident = token->ident; - nextToken(); + nextToken(); + break; + case TOKfloat32v: + case TOKfloat64v: + case TOKfloat80v: + // %% need different types? + e = new RealExp(stmt->loc, token->float80value, Type::tfloat80); + nextToken(); + break; + case TOKidentifier: { + ident = token->ident; + nextToken(); - if ( ident == Id::__LOCAL_SIZE ) - { - return new IdentifierExp ( stmt->loc, ident ); - } - else if ( ident == Id::dollar ) - { - do_dollar: - return new IdentifierExp ( stmt->loc, ident ); - } - else - { - e = new IdentifierExp ( stmt->loc, ident ); - } + if (ident == Id::__LOCAL_SIZE) { + return new IdentifierExp(stmt->loc, ident); + } else if (ident == Id::dollar) { + do_dollar: + return new IdentifierExp(stmt->loc, ident); + } else { + e = new IdentifierExp(stmt->loc, ident); + } - // If this is more than one component ref, it gets complicated: *(&Field + n) - // maybe just do one step at a time.. - // simple case is Type.f -> VarDecl(field) - // actually, DMD only supports on level... - // X.y+Y.z[EBX] is supported, tho.. - // %% doesn't handle properties (check%%) - while ( token->value == TOKdot ) - { - nextToken(); - if ( token->value == TOKidentifier ) - { - e = new DotIdExp ( stmt->loc, e, token->ident ); - nextToken(); - } - else - { - stmt->error ( "expected identifier" ); - return Handled; - } - } - - // check for reg first then dotexp is an error? - if ( e->op == TOKidentifier ) - { - for ( int i = 0; i < N_Regs; i++ ) - { - if ( ident == regInfo[i].ident ) - { - if ( ( Reg ) i == Reg_ST && token->value == TOKlparen ) - { - nextToken(); - switch ( token->value ) - { - case TOKint32v: case TOKuns32v: - case TOKint64v: case TOKuns64v: - if ( token->uns64value < 8 ) - e = newRegExp ( ( Reg ) ( Reg_ST + token->uns64value ) ); - else - { - stmt->error ( "invalid floating point register index" ); - e = Handled; - } - nextToken(); - if ( token->value == TOKrparen ) - nextToken(); - else - stmt->error ( "expected ')'" ); - return e; - default: - break; - } - invalidExpression(); - return Handled; - } - else if ( token->value == TOKcolon ) - { - nextToken(); - if ( operand->segmentPrefix != Reg_Invalid ) - stmt->error ( "too many segment prefixes" ); - else if ( i >= Reg_CS && i <= Reg_GS ) - operand->segmentPrefix = ( Reg ) i; - else - stmt->error ( "'%s' is not a segment register", ident->string ); - return parseAsmExp(); - } - else - { - return newRegExp ( ( Reg ) i ); - } - } - } - } - - if ( e->op == TOKidentifier ) - { - // DMD uses labels secondarily to other symbols, so check - // if IdentifierExp::semantic won't find anything. - Dsymbol *scopesym; - if ( ! sc->search ( stmt->loc, ident, & scopesym ) ) - { - if ( LabelDsymbol *labelsym = sc->func->searchLabel ( ident ) ) - { - e = new DsymbolExp ( stmt->loc, labelsym ); - if ( opTakesLabel() ) - return e; - return new AddrExp ( stmt->loc, e ); - } - } - } - - e = e->semantic ( sc ); - e = e->optimize (WANTvalue); - - // Special case for floating point constant declarations. - if ( e->op == TOKfloat64 ) - { - Dsymbol * sym = sc->search ( stmt->loc, ident, NULL ); - if ( sym ) - { - VarDeclaration *v = sym->isVarDeclaration(); - if ( v ) - { - Expression *ve = new VarExp ( stmt->loc, v ); - ve->type = e->type; - e = ve; - } - } - } - return e; - } - break; - case TOKdollar: - nextToken(); - ident = Id::dollar; - goto do_dollar; - break; - default: - if ( op == Op_FMath0 || op == Op_FdST0ST1 || op == Op_FMath ) - return Handled; - // DMD does not emit an error message here. - // invalidExpression(); - return Handled; - } - return e; + // If this is more than one component ref, it gets complicated: *(&Field + + // n) + // maybe just do one step at a time.. + // simple case is Type.f -> VarDecl(field) + // actually, DMD only supports on level... + // X.y+Y.z[EBX] is supported, tho.. + // %% doesn't handle properties (check%%) + while (token->value == TOKdot) { + nextToken(); + if (token->value == TOKidentifier) { + e = new DotIdExp(stmt->loc, e, token->ident); + nextToken(); + } else { + stmt->error("expected identifier"); + return Handled; } + } - void doAlign() - { - // .align bits vs. bytes... - // apparently a.out platforms use bits instead of bytes... + // check for reg first then dotexp is an error? + if (e->op == TOKidentifier) { + for (int i = 0; i < N_Regs; i++) { + if (ident == regInfo[i].ident) { + if ((Reg)i == Reg_ST && token->value == TOKlparen) { + nextToken(); + switch (token->value) { + case TOKint32v: + case TOKuns32v: + case TOKint64v: + case TOKuns64v: + if (token->uns64value < 8) + e = newRegExp((Reg)(Reg_ST + token->uns64value)); + else { + stmt->error("invalid floating point register index"); + e = Handled; + } + nextToken(); + if (token->value == TOKrparen) + nextToken(); + else + stmt->error("expected ')'"); + return e; + default: + break; + } + invalidExpression(); + return Handled; + } else if (token->value == TOKcolon) { + nextToken(); + if (operand->segmentPrefix != Reg_Invalid) + stmt->error("too many segment prefixes"); + else if (i >= Reg_CS && i <= Reg_GS) + operand->segmentPrefix = (Reg)i; + else + stmt->error("'%s' is not a segment register", ident->string); + return parseAsmExp(); + } else { + return newRegExp((Reg)i); + } + } + } + } - // parse primary: DMD allows 'MyAlign' (const int) but not '2+2' - // GAS is padding with NOPs last time I checked. - Expression * e = parseAsmExp()->ctfeInterpret(); - uinteger_t align = e->toUInteger(); + if (e->op == TOKidentifier) { + // DMD uses labels secondarily to other symbols, so check + // if IdentifierExp::semantic won't find anything. + Dsymbol *scopesym; + if (!sc->search(stmt->loc, ident, &scopesym)) { + if (LabelDsymbol *labelsym = sc->func->searchLabel(ident)) { + e = new DsymbolExp(stmt->loc, labelsym); + if (opTakesLabel()) + return e; + return new AddrExp(stmt->loc, e); + } + } + } - if ( ( align & ( align - 1 ) ) == 0 ) - { - //FIXME: This printf is not portable. The use of `align` varies from system to system; - // on i386 using a.out, .align `n` will align on a 2^`n` boundary instead of an `n` boundary + e = e->semantic(sc); + e = e->optimize(WANTvalue); + + // Special case for floating point constant declarations. + if (e->op == TOKfloat64) { + Dsymbol *sym = sc->search(stmt->loc, ident, NULL); + if (sym) { + VarDeclaration *v = sym->isVarDeclaration(); + if (v) { + Expression *ve = new VarExp(stmt->loc, v); + ve->type = e->type; + e = ve; + } + } + } + return e; + } break; + case TOKdollar: + nextToken(); + ident = Id::dollar; + goto do_dollar; + break; + default: + if (op == Op_FMath0 || op == Op_FdST0ST1 || op == Op_FMath) + return Handled; + // DMD does not emit an error message here. + // invalidExpression(); + return Handled; + } + return e; + } + + void doAlign() { + // .align bits vs. bytes... + // apparently a.out platforms use bits instead of bytes... + + // parse primary: DMD allows 'MyAlign' (const int) but not '2+2' + // GAS is padding with NOPs last time I checked. + Expression *e = parseAsmExp()->ctfeInterpret(); + uinteger_t align = e->toUInteger(); + + if ((align & (align - 1)) == 0) { +// FIXME: This printf is not portable. The use of `align` varies from system to +// system; +// on i386 using a.out, .align `n` will align on a 2^`n` boundary instead of an +// `n` boundary #ifdef HAVE_GAS_BALIGN_AND_P2ALIGN - insnTemplate << ".balign\t" << align; + insnTemplate << ".balign\t" << align; #else - insnTemplate << ".align\t" << align; + insnTemplate << ".align\t" << align; #endif - } - else - { - stmt->error ( "alignment must be a power of 2, not %u", ( unsigned ) align ); - } + } else { + stmt->error("alignment must be a power of 2, not %u", (unsigned)align); + } - setAsmCode(); - } + setAsmCode(); + } - void doEven() - { - // .align for GAS is in bits, others probably use bytes.. + void doEven() { +// .align for GAS is in bits, others probably use bytes.. #ifdef HAVE_GAS_BALIGN_AND_P2ALIGN - insnTemplate << ".align\t2"; + insnTemplate << ".align\t2"; #else - insnTemplate << ".align\t2"; + insnTemplate << ".align\t2"; #endif - setAsmCode(); - } + setAsmCode(); + } - void doNaked() - { - // %% can we assume sc->func != 0? - sc->func->naked = 1; - } + void doNaked() { + // %% can we assume sc->func != 0? + sc->func->naked = 1; + } - void doData() - { - // FIXME: data instructions not implemented. + void doData() { +// FIXME: data instructions not implemented. #if 0 static const char * directives[] = { ".byte", ".short", ".long", ".long", "", "", "" @@ -3893,9 +3829,9 @@ namespace AsmParserx8664 mode = DFmode; goto do_float; case Op_de: - #ifndef TARGET_80387 - #define XFmode TFmode - #endif +#ifndef TARGET_80387 +#define XFmode TFmode +#endif mode = XFmode; // not TFmode // drop through do_float: @@ -3931,67 +3867,60 @@ namespace AsmParserx8664 setAsmCode(); #endif - } - }; + } +}; #if D_GCC_VER < 40 // struct rtx was modified for c++; this macro from rtl.h needs to // be modified accordingly. #undef XEXP -#define XEXP(RTX, N) (RTL_CHECK2 (RTX, N, 'e', 'u').rt_rtx) +#define XEXP(RTX, N) (RTL_CHECK2(RTX, N, 'e', 'u').rt_rtx) #endif // FIXME #define HOST_WIDE_INT long - bool getFrameRelativeValue ( LLValue* decl, HOST_WIDE_INT * result ) - { - llvm_unreachable("getFrameRelativeValue not implemented."); -// FIXME -// // Using this instead of DECL_RTL for struct args seems like a -// // good way to get hit by a truck because it may not agree with -// // non-asm access, but asm code wouldn't know what GCC does anyway. */ -// rtx r = DECL_INCOMING_RTL(decl); -// rtx e1, e2; -// -// // Local variables don't have DECL_INCOMING_RTL -// if (r == NULL_RTX) -// r = DECL_RTL(decl); -// -// if (r != NULL_RTX && GET_CODE(r) == MEM /* && r->frame_related */ ) { -// r = XEXP(r, 0); -// if (GET_CODE(r) == PLUS) { -// e1 = XEXP(r, 0); -// e2 = XEXP(r, 1); -// if (e1 == virtual_incoming_args_rtx && GET_CODE(e2) == CONST_INT) { -// *result = INTVAL(e2) + 8; // %% 8 is 32-bit specific... -// return true; -// } else if (e1 == virtual_stack_vars_rtx && GET_CODE(e2) == CONST_INT) { -// *result = INTVAL(e2); // %% 8 is 32-bit specific... -// return true; -// } -// } else if (r == virtual_incoming_args_rtx) { -// *result = 8; -// return true; // %% same as above -// } -// // shouldn't have virtual_stack_vars_rtx by itself -// } -// - return false; - } - - - struct AsmParser : public AsmParserCommon - { - virtual void run ( Scope* sc, AsmStatement* asmst ) - { - AsmProcessor ap ( sc, asmst ); - ap.run(); - } - - virtual std::string getRegName ( int i ) - { - return regInfo[i].gccName; - } - }; - +bool getFrameRelativeValue(LLValue *decl, HOST_WIDE_INT *result) { + llvm_unreachable("getFrameRelativeValue not implemented."); + // FIXME + // // Using this instead of DECL_RTL for struct args seems like a + // // good way to get hit by a truck because it may not agree with + // // non-asm access, but asm code wouldn't know what GCC does anyway. */ + // rtx r = DECL_INCOMING_RTL(decl); + // rtx e1, e2; + // + // // Local variables don't have DECL_INCOMING_RTL + // if (r == NULL_RTX) + // r = DECL_RTL(decl); + // + // if (r != NULL_RTX && GET_CODE(r) == MEM /* && r->frame_related */ ) { + // r = XEXP(r, 0); + // if (GET_CODE(r) == PLUS) { + // e1 = XEXP(r, 0); + // e2 = XEXP(r, 1); + // if (e1 == virtual_incoming_args_rtx && GET_CODE(e2) == CONST_INT) { + // *result = INTVAL(e2) + 8; // %% 8 is 32-bit specific... + // return true; + // } else if (e1 == virtual_stack_vars_rtx && GET_CODE(e2) == CONST_INT) + // { + // *result = INTVAL(e2); // %% 8 is 32-bit specific... + // return true; + // } + // } else if (r == virtual_incoming_args_rtx) { + // *result = 8; + // return true; // %% same as above + // } + // // shouldn't have virtual_stack_vars_rtx by itself + // } + // + return false; +} + +struct AsmParser : public AsmParserCommon { + virtual void run(Scope *sc, AsmStatement *asmst) { + AsmProcessor ap(sc, asmst); + ap.run(); + } + + virtual std::string getRegName(int i) { return regInfo[i].gccName; } +}; } diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index 1e41122342..c4c4fa0d6d 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -36,73 +36,65 @@ #include "ir/irfunction.h" typedef enum { - Arg_Integer, - Arg_Pointer, - Arg_Memory, - Arg_FrameRelative, - Arg_LocalSize, - Arg_Dollar + Arg_Integer, + Arg_Pointer, + Arg_Memory, + Arg_FrameRelative, + Arg_LocalSize, + Arg_Dollar } AsmArgType; -typedef enum { - Mode_Input, - Mode_Output, - Mode_Update -} AsmArgMode; +typedef enum { Mode_Input, Mode_Output, Mode_Update } AsmArgMode; struct AsmArg { - Expression * expr; - AsmArgType type; - AsmArgMode mode; - AsmArg(AsmArgType type, Expression * expr, AsmArgMode mode) { - this->type = type; - this->expr = expr; - this->mode = mode; - } + Expression *expr; + AsmArgType type; + AsmArgMode mode; + AsmArg(AsmArgType type, Expression *expr, AsmArgMode mode) { + this->type = type; + this->expr = expr; + this->mode = mode; + } }; struct AsmCode { - std::string insnTemplate; - std::vector args; - std::vector regs; - unsigned dollarLabel; - int clobbersMemory; - AsmCode(int n_regs) { - regs.resize(n_regs, false); - dollarLabel = 0; - clobbersMemory = 0; - } + std::string insnTemplate; + std::vector args; + std::vector regs; + unsigned dollarLabel; + int clobbersMemory; + AsmCode(int n_regs) { + regs.resize(n_regs, false); + dollarLabel = 0; + clobbersMemory = 0; + } }; -AsmStatement::AsmStatement(Loc loc, Token *tokens) : - Statement(loc) -{ - this->tokens = tokens; // Do I need to copy these? - asmcode = 0; - asmalign = 0; - refparam = 0; - naked = 0; +AsmStatement::AsmStatement(Loc loc, Token *tokens) : Statement(loc) { + this->tokens = tokens; // Do I need to copy these? + asmcode = 0; + asmalign = 0; + refparam = 0; + naked = 0; - isBranchToLabel = NULL; + isBranchToLabel = NULL; } -Statement *AsmStatement::syntaxCopy() -{ - // copy tokens? copy 'code'? - AsmStatement * a_s = new AsmStatement(loc,tokens); - a_s->asmcode = asmcode; - a_s->refparam = refparam; - a_s->naked = naked; - return a_s; +Statement *AsmStatement::syntaxCopy() { + // copy tokens? copy 'code'? + AsmStatement *a_s = new AsmStatement(loc, tokens); + a_s->asmcode = asmcode; + a_s->refparam = refparam; + a_s->naked = naked; + return a_s; } -struct AsmParserCommon -{ - virtual ~AsmParserCommon() {} - virtual void run(Scope* sc, AsmStatement* asmst) = 0; - virtual std::string getRegName(int i) = 0; +struct AsmParserCommon { + virtual ~AsmParserCommon() {} + virtual void run(Scope *sc, AsmStatement *asmst) = 0; + virtual std::string getRegName(int i) = 0; }; -AsmParserCommon* asmparser = NULL; +AsmParserCommon *asmparser = NULL; #include "asm-x86.h" // x86 assembly parser #define ASM_X86_64 @@ -117,674 +109,662 @@ AsmParserCommon* asmparser = NULL; * might change the function type (and hence the mangled name) right at the end * of semantic3. */ -static void replace_func_name(IRState* p, std::string& insnt) -{ - static const std::string needle("<>"); +static void replace_func_name(IRState *p, std::string &insnt) { + static const std::string needle("<>"); - size_t pos; - while (std::string::npos != (pos = insnt.find(needle))) - { - // This will only happen for few instructions, and only once for those. - insnt.replace(pos, needle.size(), mangle(p->func()->decl)); - } + size_t pos; + while (std::string::npos != (pos = insnt.find(needle))) { + // This will only happen for few instructions, and only once for those. + insnt.replace(pos, needle.size(), mangle(p->func()->decl)); + } } -Statement* asmSemantic(AsmStatement *s, Scope *sc) -{ - bool err = false; - llvm::Triple const t = global.params.targetTriple; - if (!(t.getArch() == llvm::Triple::x86 || t.getArch() == llvm::Triple::x86_64)) - { - s->error("inline asm is not supported for the \"%s\" architecture", - t.getArchName().str().c_str()); - err = true; - } - if (!global.params.useInlineAsm) - { - s->error("inline asm is not allowed when the -noasm switch is used"); - err = true; - } - if (err) - fatal(); +Statement *asmSemantic(AsmStatement *s, Scope *sc) { + bool err = false; + llvm::Triple const t = global.params.targetTriple; + if (!(t.getArch() == llvm::Triple::x86 || + t.getArch() == llvm::Triple::x86_64)) { + s->error("inline asm is not supported for the \"%s\" architecture", + t.getArchName().str().c_str()); + err = true; + } + if (!global.params.useInlineAsm) { + s->error("inline asm is not allowed when the -noasm switch is used"); + err = true; + } + if (err) + fatal(); - //puts(toChars()); + // puts(toChars()); - sc->func->hasReturnExp |= 8; - - // empty statement -- still do the above things because they might be expected? - if (!s->tokens) - return s; - - if (!asmparser) - { - if (t.getArch() == llvm::Triple::x86) - asmparser = new AsmParserx8632::AsmParser; - else if (t.getArch() == llvm::Triple::x86_64) - asmparser = new AsmParserx8664::AsmParser; - } - - asmparser->run(sc, s); + sc->func->hasReturnExp |= 8; + // empty statement -- still do the above things because they might be + // expected? + if (!s->tokens) return s; + + if (!asmparser) { + if (t.getArch() == llvm::Triple::x86) + asmparser = new AsmParserx8632::AsmParser; + else if (t.getArch() == llvm::Triple::x86_64) + asmparser = new AsmParserx8664::AsmParser; + } + + asmparser->run(sc, s); + + return s; } -void AsmStatement_toIR(AsmStatement *stmt, IRState * irs) -{ - IF_LOG Logger::println("AsmStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; +void AsmStatement_toIR(AsmStatement *stmt, IRState *irs) { + IF_LOG Logger::println("AsmStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; - // sanity check - assert(irs->func()->decl->hasReturnExp & 8); + // sanity check + assert(irs->func()->decl->hasReturnExp & 8); - // get asm block - IRAsmBlock* asmblock = irs->asmBlock; - assert(asmblock); + // get asm block + IRAsmBlock *asmblock = irs->asmBlock; + assert(asmblock); - // debug info - gIR->DBuilder.EmitStopPoint(stmt->loc); + // debug info + gIR->DBuilder.EmitStopPoint(stmt->loc); - if (!stmt->asmcode) - return; + if (!stmt->asmcode) + return; - static std::string i_cns = "i"; - static std::string p_cns = "i"; - static std::string m_cns = "*m"; - static std::string mw_cns = "=*m"; - static std::string mrw_cns = "+*m"; - static std::string memory_name = "memory"; + static std::string i_cns = "i"; + static std::string p_cns = "i"; + static std::string m_cns = "*m"; + static std::string mw_cns = "=*m"; + static std::string mrw_cns = "+*m"; + static std::string memory_name = "memory"; - AsmCode *code = static_cast(stmt->asmcode); - std::vector input_values; - std::vector input_constraints; - std::vector output_values; - std::vector output_constraints; - std::vector clobbers; + AsmCode *code = static_cast(stmt->asmcode); + std::vector input_values; + std::vector input_constraints; + std::vector output_values; + std::vector output_constraints; + std::vector clobbers; -// FIXME - //#define HOST_WIDE_INT long - //HOST_WIDE_INT var_frame_offset; // "frame_offset" is a macro - bool clobbers_mem = code->clobbersMemory; - int input_idx = 0; - int n_outputs = 0; - int arg_map[10]; + // FIXME + //#define HOST_WIDE_INT long + // HOST_WIDE_INT var_frame_offset; // "frame_offset" is a macro + bool clobbers_mem = code->clobbersMemory; + int input_idx = 0; + int n_outputs = 0; + int arg_map[10]; - assert(code->args.size() <= 10); + assert(code->args.size() <= 10); - auto arg = code->args.begin(); - for (unsigned i = 0; i < code->args.size(); i++, ++arg) { - bool is_input = true; - LLValue* arg_val = 0; - std::string cns; + auto arg = code->args.begin(); + for (unsigned i = 0; i < code->args.size(); i++, ++arg) { + bool is_input = true; + LLValue *arg_val = 0; + std::string cns; - switch (arg->type) { - case Arg_Integer: - arg_val = toElem(arg->expr)->getRVal(); - do_integer: - cns = i_cns; - break; - case Arg_Pointer: - assert(arg->expr->op == TOKvar); - arg_val = toElem(arg->expr)->getRVal(); - cns = p_cns; + switch (arg->type) { + case Arg_Integer: + arg_val = toElem(arg->expr)->getRVal(); + do_integer: + cns = i_cns; + break; + case Arg_Pointer: + assert(arg->expr->op == TOKvar); + arg_val = toElem(arg->expr)->getRVal(); + cns = p_cns; - break; - case Arg_Memory: - arg_val = toElem(arg->expr)->getRVal(); + break; + case Arg_Memory: + arg_val = toElem(arg->expr)->getRVal(); - switch (arg->mode) { - case Mode_Input: cns = m_cns; break; - case Mode_Output: cns = mw_cns; is_input = false; break; - case Mode_Update: cns = mrw_cns; is_input = false; break; - default: llvm_unreachable("Unknown inline asm reference mode."); break; - } - break; - case Arg_FrameRelative: - // FIXME - llvm_unreachable("Arg_FrameRelative not supported."); -/* if (arg->expr->op == TOKvar) - arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree; - else - assert(0); - if ( getFrameRelativeValue(arg_val, & var_frame_offset) ) { -// arg_val = irs->integerConstant(var_frame_offset); - cns = i_cns; - } else { - this->error("%s", "argument not frame relative"); - return; - } - if (arg->mode != Mode_Input) - clobbers_mem = true; - break;*/ - case Arg_LocalSize: - // FIXME - llvm_unreachable("Arg_LocalSize not supported."); -/* var_frame_offset = cfun->x_frame_offset; - if (var_frame_offset < 0) - var_frame_offset = - var_frame_offset; - arg_val = irs->integerConstant( var_frame_offset );*/ - goto do_integer; - default: - llvm_unreachable("Unknown inline asm reference type."); - } - - if (is_input) { - arg_map[i] = --input_idx; - input_values.push_back(arg_val); - input_constraints.push_back(cns); - } else { - arg_map[i] = n_outputs++; - output_values.push_back(arg_val); - output_constraints.push_back(cns); - } + switch (arg->mode) { + case Mode_Input: + cns = m_cns; + break; + case Mode_Output: + cns = mw_cns; + is_input = false; + break; + case Mode_Update: + cns = mrw_cns; + is_input = false; + break; + default: + llvm_unreachable("Unknown inline asm reference mode."); + break; + } + break; + case Arg_FrameRelative: + // FIXME + llvm_unreachable("Arg_FrameRelative not supported."); + /* if (arg->expr->op == TOKvar) + arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree; + else + assert(0); + if ( getFrameRelativeValue(arg_val, & var_frame_offset) ) { + // arg_val = irs->integerConstant(var_frame_offset); + cns = i_cns; + } else { + this->error("%s", "argument not frame relative"); + return; + } + if (arg->mode != Mode_Input) + clobbers_mem = true; + break;*/ + case Arg_LocalSize: + // FIXME + llvm_unreachable("Arg_LocalSize not supported."); + /* var_frame_offset = cfun->x_frame_offset; + if (var_frame_offset < 0) + var_frame_offset = - var_frame_offset; + arg_val = irs->integerConstant( var_frame_offset );*/ + goto do_integer; + default: + llvm_unreachable("Unknown inline asm reference type."); } - // Telling GCC that callee-saved registers are clobbered makes it preserve - // those registers. This changes the stack from what a naked function - // expects. - -// FIXME -// if (! irs->func->naked) { - assert(asmparser); - for (size_t i = 0; i < code->regs.size(); i++) { - if (code->regs[i]) { - clobbers.push_back(asmparser->getRegName(i)); - } - } - if (clobbers_mem) - clobbers.push_back(memory_name); -// } - - // Remap argument numbers - for (unsigned i = 0; i < code->args.size(); i++) { - if (arg_map[i] < 0) - arg_map[i] = -arg_map[i] - 1 + n_outputs; + if (is_input) { + arg_map[i] = --input_idx; + input_values.push_back(arg_val); + input_constraints.push_back(cns); + } else { + arg_map[i] = n_outputs++; + output_values.push_back(arg_val); + output_constraints.push_back(cns); } + } - bool pct = false; - auto p = code->insnTemplate.begin(); - auto q = code->insnTemplate.end(); - //printf("start: %.*s\n", code->insnTemplateLen, code->insnTemplate); - while (p < q) { - if (pct) { - if (*p >= '0' && *p <= '9') { - // %% doesn't check against nargs - *p = '0' + arg_map[*p - '0']; - pct = false; - } else if (*p == '$') { - pct = false; - } - //assert(*p == '%');// could be 'a', etc. so forget it.. - } else if (*p == '$') - pct = true; - ++p; + // Telling GCC that callee-saved registers are clobbered makes it preserve + // those registers. This changes the stack from what a naked function + // expects. + + // FIXME + // if (! irs->func->naked) { + assert(asmparser); + for (size_t i = 0; i < code->regs.size(); i++) { + if (code->regs[i]) { + clobbers.push_back(asmparser->getRegName(i)); } + } + if (clobbers_mem) + clobbers.push_back(memory_name); + // } - IF_LOG { - Logger::cout() << "final asm: " << code->insnTemplate << '\n'; - std::ostringstream ss; + // Remap argument numbers + for (unsigned i = 0; i < code->args.size(); i++) { + if (arg_map[i] < 0) + arg_map[i] = -arg_map[i] - 1 + n_outputs; + } - ss << "GCC-style output constraints: {"; - for (const auto& oc : output_constraints) { - ss << " " << oc; - } - ss << " }"; - Logger::println("%s", ss.str().c_str()); + bool pct = false; + auto p = code->insnTemplate.begin(); + auto q = code->insnTemplate.end(); + // printf("start: %.*s\n", code->insnTemplateLen, code->insnTemplate); + while (p < q) { + if (pct) { + if (*p >= '0' && *p <= '9') { + // %% doesn't check against nargs + *p = '0' + arg_map[*p - '0']; + pct = false; + } else if (*p == '$') { + pct = false; + } + // assert(*p == '%');// could be 'a', etc. so forget it.. + } else if (*p == '$') + pct = true; + ++p; + } - ss.str(""); - ss << "GCC-style input constraints: {"; - for (const auto& ic : input_constraints) { - ss << " " << ic; - } - ss << " }"; - Logger::println("%s", ss.str().c_str()); + IF_LOG { + Logger::cout() << "final asm: " << code->insnTemplate << '\n'; + std::ostringstream ss; - ss.str(""); - ss << "GCC-style clobbers: {"; - for (const auto& c : clobbers) { - ss << " " << c; - } - ss << " }"; - Logger::println("%s", ss.str().c_str()); + ss << "GCC-style output constraints: {"; + for (const auto &oc : output_constraints) { + ss << " " << oc; } + ss << " }"; + Logger::println("%s", ss.str().c_str()); - // rewrite GCC-style constraints to LLVM-style constraints - std::string llvmOutConstraints; - std::string llvmInConstraints; - int n = 0; - for (auto& oc : output_constraints) { - // rewrite update constraint to in and out constraints - if(oc[0] == '+') { - assert(oc == mrw_cns && "What else are we updating except memory?"); - /* LLVM doesn't support updating operands, so split into an input - * and an output operand. - */ - - // Change update operand to pure output operand. - oc = mw_cns; - - // Add input operand with same value, with original as "matching output". - std::ostringstream ss; - ss << '*' << (n + asmblock->outputcount); - // Must be at the back; unused operands before used ones screw up numbering. - input_constraints.push_back(ss.str()); - input_values.push_back(output_values[n]); - } - llvmOutConstraints += oc; - llvmOutConstraints += ","; + ss.str(""); + ss << "GCC-style input constraints: {"; + for (const auto &ic : input_constraints) { + ss << " " << ic; } - asmblock->outputcount += n; + ss << " }"; + Logger::println("%s", ss.str().c_str()); - for (const auto& ic : input_constraints) { - llvmInConstraints += ic; - llvmInConstraints += ","; + ss.str(""); + ss << "GCC-style clobbers: {"; + for (const auto &c : clobbers) { + ss << " " << c; } + ss << " }"; + Logger::println("%s", ss.str().c_str()); + } - std::string clobstr; - for (const auto& c : clobbers) { - clobstr = "~{" + c + "},"; - asmblock->clobs.insert(clobstr); + // rewrite GCC-style constraints to LLVM-style constraints + std::string llvmOutConstraints; + std::string llvmInConstraints; + int n = 0; + for (auto &oc : output_constraints) { + // rewrite update constraint to in and out constraints + if (oc[0] == '+') { + assert(oc == mrw_cns && "What else are we updating except memory?"); + /* LLVM doesn't support updating operands, so split into an input + * and an output operand. + */ + + // Change update operand to pure output operand. + oc = mw_cns; + + // Add input operand with same value, with original as "matching output". + std::ostringstream ss; + ss << '*' << (n + asmblock->outputcount); + // Must be at the back; unused operands before used ones screw up + // numbering. + input_constraints.push_back(ss.str()); + input_values.push_back(output_values[n]); } + llvmOutConstraints += oc; + llvmOutConstraints += ","; + } + asmblock->outputcount += n; - IF_LOG { - { - Logger::println("Output values:"); - LOG_SCOPE - size_t i = 0; - for (auto ov : output_values) { - Logger::cout() << "Out " << i++ << " = " << *ov << '\n'; - } - } - { - Logger::println("Input values:"); - LOG_SCOPE - size_t i = 0; - for (auto iv : input_values) { - Logger::cout() << "In " << i++ << " = " << *iv << '\n'; - } - } + for (const auto &ic : input_constraints) { + llvmInConstraints += ic; + llvmInConstraints += ","; + } + + std::string clobstr; + for (const auto &c : clobbers) { + clobstr = "~{" + c + "},"; + asmblock->clobs.insert(clobstr); + } + + IF_LOG { + { + Logger::println("Output values:"); + LOG_SCOPE + size_t i = 0; + for (auto ov : output_values) { + Logger::cout() << "Out " << i++ << " = " << *ov << '\n'; + } } + { + Logger::println("Input values:"); + LOG_SCOPE + size_t i = 0; + for (auto iv : input_values) { + Logger::cout() << "In " << i++ << " = " << *iv << '\n'; + } + } + } - // excessive commas are removed later... + // excessive commas are removed later... - replace_func_name(irs, code->insnTemplate); + replace_func_name(irs, code->insnTemplate); - // push asm statement - IRAsmStmt* asmStmt = new IRAsmStmt; - asmStmt->code = code->insnTemplate; - asmStmt->out_c = llvmOutConstraints; - asmStmt->in_c = llvmInConstraints; - asmStmt->out.insert(asmStmt->out.begin(), output_values.begin(), output_values.end()); - asmStmt->in.insert(asmStmt->in.begin(), input_values.begin(), input_values.end()); - asmStmt->isBranchToLabel = stmt->isBranchToLabel; - asmblock->s.push_back(asmStmt); + // push asm statement + IRAsmStmt *asmStmt = new IRAsmStmt; + asmStmt->code = code->insnTemplate; + asmStmt->out_c = llvmOutConstraints; + asmStmt->in_c = llvmInConstraints; + asmStmt->out.insert(asmStmt->out.begin(), output_values.begin(), + output_values.end()); + asmStmt->in.insert(asmStmt->in.begin(), input_values.begin(), + input_values.end()); + asmStmt->isBranchToLabel = stmt->isBranchToLabel; + asmblock->s.push_back(asmStmt); } ////////////////////////////////////////////////////////////////////////////// // rewrite argument indices to the block scope indices -static void remap_outargs(std::string& insnt, size_t nargs, size_t idx) -{ - static const std::string digits[10] = - { - "0","1","2","3","4", - "5","6","7","8","9" - }; - assert(nargs <= 10); +static void remap_outargs(std::string &insnt, size_t nargs, size_t idx) { + static const std::string digits[10] = {"0", "1", "2", "3", "4", + "5", "6", "7", "8", "9"}; + assert(nargs <= 10); - static const std::string prefix("<>"); - std::string argnum; - std::string needle; - char buf[10]; - for (unsigned i = 0; i < nargs; i++) { - needle = prefix + digits[i] + suffix; - size_t pos = insnt.find(needle); - if(std::string::npos != pos) - sprintf(buf, "%llu", static_cast(idx++)); - while(std::string::npos != (pos = insnt.find(needle))) - insnt.replace(pos, needle.size(), buf); - } + static const std::string prefix("<>"); + std::string argnum; + std::string needle; + char buf[10]; + for (unsigned i = 0; i < nargs; i++) { + needle = prefix + digits[i] + suffix; + size_t pos = insnt.find(needle); + if (std::string::npos != pos) + sprintf(buf, "%llu", static_cast(idx++)); + while (std::string::npos != (pos = insnt.find(needle))) + insnt.replace(pos, needle.size(), buf); + } } // rewrite argument indices to the block scope indices -static void remap_inargs(std::string& insnt, size_t nargs, size_t idx) -{ - static const std::string digits[10] = - { - "0","1","2","3","4", - "5","6","7","8","9" - }; - assert(nargs <= 10); +static void remap_inargs(std::string &insnt, size_t nargs, size_t idx) { + static const std::string digits[10] = {"0", "1", "2", "3", "4", + "5", "6", "7", "8", "9"}; + assert(nargs <= 10); - static const std::string prefix("<>"); - std::string argnum; - std::string needle; - char buf[10]; - for (unsigned i = 0; i < nargs; i++) { - needle = prefix + digits[i] + suffix; - size_t pos = insnt.find(needle); - if(std::string::npos != pos) - sprintf(buf, "%llu", static_cast(idx++)); - while(std::string::npos != (pos = insnt.find(needle))) - insnt.replace(pos, needle.size(), buf); - } + static const std::string prefix("<>"); + std::string argnum; + std::string needle; + char buf[10]; + for (unsigned i = 0; i < nargs; i++) { + needle = prefix + digits[i] + suffix; + size_t pos = insnt.find(needle); + if (std::string::npos != pos) + sprintf(buf, "%llu", static_cast(idx++)); + while (std::string::npos != (pos = insnt.find(needle))) + insnt.replace(pos, needle.size(), buf); + } } -void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState* p) -{ - IF_LOG Logger::println("CompoundAsmStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; +void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) { + IF_LOG Logger::println("CompoundAsmStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; - // disable inlining by default - if (!p->func()->decl->allowInlining) - p->func()->setNeverInline(); + // disable inlining by default + if (!p->func()->decl->allowInlining) + p->func()->setNeverInline(); - // create asm block structure - assert(!p->asmBlock); - IRAsmBlock* asmblock = new IRAsmBlock(stmt); - assert(asmblock); - p->asmBlock = asmblock; + // create asm block structure + assert(!p->asmBlock); + IRAsmBlock *asmblock = new IRAsmBlock(stmt); + assert(asmblock); + p->asmBlock = asmblock; - // do asm statements - for (unsigned i=0; i < stmt->statements->dim; i++) - { - Statement* s = (*stmt->statements)[i]; - if (s) { - Statement_toIR(s, p); - } + // do asm statements + for (unsigned i = 0; i < stmt->statements->dim; i++) { + Statement *s = (*stmt->statements)[i]; + if (s) { + Statement_toIR(s, p); } + } - // build forwarder for in-asm branches to external labels - // this additional asm code sets the __llvm_jump_target variable - // to a unique value that will identify the jump target in - // a post-asm switch + // build forwarder for in-asm branches to external labels + // this additional asm code sets the __llvm_jump_target variable + // to a unique value that will identify the jump target in + // a post-asm switch - // maps each goto destination to its special value - std::map gotoToVal; + // maps each goto destination to its special value + std::map gotoToVal; - // location of the special value determining the goto label - // will be set if post-asm dispatcher block is needed - LLValue* jump_target = 0; + // location of the special value determining the goto label + // will be set if post-asm dispatcher block is needed + LLValue *jump_target = 0; - { - FuncDeclaration* fd = gIR->func()->decl; - const char* fdmangle = mangle(fd); + { + FuncDeclaration *fd = gIR->func()->decl; + const char *fdmangle = mangle(fd); - // we use a simple static counter to make sure the new end labels are unique - static size_t uniqueLabelsId = 0; - std::ostringstream asmGotoEndLabel; - printLabelName(asmGotoEndLabel, fdmangle, "_llvm_asm_end"); - asmGotoEndLabel << uniqueLabelsId++; + // we use a simple static counter to make sure the new end labels are unique + static size_t uniqueLabelsId = 0; + std::ostringstream asmGotoEndLabel; + printLabelName(asmGotoEndLabel, fdmangle, "_llvm_asm_end"); + asmGotoEndLabel << uniqueLabelsId++; - // initialize the setter statement we're going to build - IRAsmStmt* outSetterStmt = new IRAsmStmt; - std::string asmGotoEnd = "\n\tjmp "+asmGotoEndLabel.str()+"\n"; - std::ostringstream code; - code << asmGotoEnd; + // initialize the setter statement we're going to build + IRAsmStmt *outSetterStmt = new IRAsmStmt; + std::string asmGotoEnd = "\n\tjmp " + asmGotoEndLabel.str() + "\n"; + std::ostringstream code; + code << asmGotoEnd; - int n_goto = 1; + int n_goto = 1; - size_t n = asmblock->s.size(); - for(size_t i=0; is[i]; - - // skip non-branch statements - if(!a->isBranchToLabel) - continue; - - // if internal, no special handling is necessary, skip - std::vector::const_iterator it, end; - end = asmblock->internalLabels.end(); - bool skip = false; - for(auto il : asmblock->internalLabels) - if(il->equals(a->isBranchToLabel->ident)) - skip = true; - if(skip) - continue; - - // if we already set things up for this branch target, skip - if(gotoToVal.find(a->isBranchToLabel) != gotoToVal.end()) - continue; - - // record that the jump needs to be handled in the post-asm dispatcher - gotoToVal[a->isBranchToLabel] = n_goto; - - // provide an in-asm target for the branch and set value - IF_LOG Logger::println("statement '%s' references outer label '%s': creating forwarder", a->code.c_str(), a->isBranchToLabel->ident->string); - printLabelName(code, fdmangle, a->isBranchToLabel->ident->string); - code << ":\n\t"; - code << "movl $<>, $<>\n"; - //FIXME: Store the value -> label mapping somewhere, so it can be referenced later - outSetterStmt->in.push_back(DtoConstUint(n_goto)); - outSetterStmt->in_c += "i,"; - code << asmGotoEnd; - - ++n_goto; - } - if(code.str() != asmGotoEnd) - { - // finalize code - outSetterStmt->code = code.str(); - outSetterStmt->code += asmGotoEndLabel.str()+":\n"; - - // create storage for and initialize the temporary - jump_target = DtoAllocaDump(DtoConstUint(0), 0, "__llvm_jump_target"); - // setup variable for output from asm - outSetterStmt->out_c = "=*m,"; - outSetterStmt->out.push_back(jump_target); - - asmblock->s.push_back(outSetterStmt); - } - else - delete outSetterStmt; - } - - - // build a fall-off-end-properly asm statement - - FuncDeclaration* thisfunc = p->func()->decl; - bool useabiret = false; - p->asmBlock->asmBlock->abiret = NULL; - if (thisfunc->fbody->endsWithAsm() == stmt && thisfunc->type->nextOf()->ty != Tvoid) - { - // there can't be goto forwarders in this case - assert(gotoToVal.empty()); - emitABIReturnAsmStmt(asmblock, stmt->loc, thisfunc); - useabiret = true; - } - - - // build asm block - std::vector outargs; - std::vector inargs; - std::vector outtypes; - std::vector intypes; - std::string out_c; - std::string in_c; - std::string clobbers; - std::string code; - size_t asmIdx = asmblock->retn; - - Logger::println("do outputs"); size_t n = asmblock->s.size(); - for (size_t i=0; is[i]; - assert(a); - size_t onn = a->out.size(); - for (size_t j=0; jout[j]); - outtypes.push_back(a->out[j]->getType()); - } - if (!a->out_c.empty()) - { - out_c += a->out_c; - } - remap_outargs(a->code, onn+a->in.size(), asmIdx); - asmIdx += onn; + for (size_t i = 0; i < n; ++i) { + IRAsmStmt *a = asmblock->s[i]; + + // skip non-branch statements + if (!a->isBranchToLabel) + continue; + + // if internal, no special handling is necessary, skip + std::vector::const_iterator it, end; + end = asmblock->internalLabels.end(); + bool skip = false; + for (auto il : asmblock->internalLabels) + if (il->equals(a->isBranchToLabel->ident)) + skip = true; + if (skip) + continue; + + // if we already set things up for this branch target, skip + if (gotoToVal.find(a->isBranchToLabel) != gotoToVal.end()) + continue; + + // record that the jump needs to be handled in the post-asm dispatcher + gotoToVal[a->isBranchToLabel] = n_goto; + + // provide an in-asm target for the branch and set value + IF_LOG Logger::println( + "statement '%s' references outer label '%s': creating forwarder", + a->code.c_str(), a->isBranchToLabel->ident->string); + printLabelName(code, fdmangle, a->isBranchToLabel->ident->string); + code << ":\n\t"; + code << "movl $<>, $<>\n"; + // FIXME: Store the value -> label mapping somewhere, so it can be + // referenced later + outSetterStmt->in.push_back(DtoConstUint(n_goto)); + outSetterStmt->in_c += "i,"; + code << asmGotoEnd; + + ++n_goto; } + if (code.str() != asmGotoEnd) { + // finalize code + outSetterStmt->code = code.str(); + outSetterStmt->code += asmGotoEndLabel.str() + ":\n"; - Logger::println("do inputs"); - for (size_t i=0; is[i]; - assert(a); - size_t inn = a->in.size(); - for (size_t j=0; jin[j]); - intypes.push_back(a->in[j]->getType()); - } - if (!a->in_c.empty()) - { - in_c += a->in_c; - } - remap_inargs(a->code, inn+a->out.size(), asmIdx); - asmIdx += inn; - if (!code.empty()) - code += "\n\t"; - code += a->code; + // create storage for and initialize the temporary + jump_target = DtoAllocaDump(DtoConstUint(0), 0, "__llvm_jump_target"); + // setup variable for output from asm + outSetterStmt->out_c = "=*m,"; + outSetterStmt->out.push_back(jump_target); + + asmblock->s.push_back(outSetterStmt); + } else + delete outSetterStmt; + } + + // build a fall-off-end-properly asm statement + + FuncDeclaration *thisfunc = p->func()->decl; + bool useabiret = false; + p->asmBlock->asmBlock->abiret = NULL; + if (thisfunc->fbody->endsWithAsm() == stmt && + thisfunc->type->nextOf()->ty != Tvoid) { + // there can't be goto forwarders in this case + assert(gotoToVal.empty()); + emitABIReturnAsmStmt(asmblock, stmt->loc, thisfunc); + useabiret = true; + } + + // build asm block + std::vector outargs; + std::vector inargs; + std::vector outtypes; + std::vector intypes; + std::string out_c; + std::string in_c; + std::string clobbers; + std::string code; + size_t asmIdx = asmblock->retn; + + Logger::println("do outputs"); + size_t n = asmblock->s.size(); + for (size_t i = 0; i < n; ++i) { + IRAsmStmt *a = asmblock->s[i]; + assert(a); + size_t onn = a->out.size(); + for (size_t j = 0; j < onn; ++j) { + outargs.push_back(a->out[j]); + outtypes.push_back(a->out[j]->getType()); } - asmblock->s.clear(); - - // append inputs - out_c += in_c; - - // append clobbers - for (const auto& c : asmblock->clobs) - { - out_c += c; + if (!a->out_c.empty()) { + out_c += a->out_c; } + remap_outargs(a->code, onn + a->in.size(), asmIdx); + asmIdx += onn; + } - // remove excessive comma - if (!out_c.empty()) - out_c.resize(out_c.size()-1); - - IF_LOG { - Logger::println("code = \"%s\"", code.c_str()); - Logger::println("constraints = \"%s\"", out_c.c_str()); + Logger::println("do inputs"); + for (size_t i = 0; i < n; ++i) { + IRAsmStmt *a = asmblock->s[i]; + assert(a); + size_t inn = a->in.size(); + for (size_t j = 0; j < inn; ++j) { + inargs.push_back(a->in[j]); + intypes.push_back(a->in[j]->getType()); } + if (!a->in_c.empty()) { + in_c += a->in_c; + } + remap_inargs(a->code, inn + a->out.size(), asmIdx); + asmIdx += inn; + if (!code.empty()) + code += "\n\t"; + code += a->code; + } + asmblock->s.clear(); - // build return types - LLType* retty; - if (asmblock->retn) - retty = asmblock->retty; + // append inputs + out_c += in_c; + + // append clobbers + for (const auto &c : asmblock->clobs) { + out_c += c; + } + + // remove excessive comma + if (!out_c.empty()) + out_c.resize(out_c.size() - 1); + + IF_LOG { + Logger::println("code = \"%s\"", code.c_str()); + Logger::println("constraints = \"%s\"", out_c.c_str()); + } + + // build return types + LLType *retty; + if (asmblock->retn) + retty = asmblock->retty; + else + retty = llvm::Type::getVoidTy(gIR->context()); + + // build argument types + std::vector types; + types.insert(types.end(), outtypes.begin(), outtypes.end()); + types.insert(types.end(), intypes.begin(), intypes.end()); + llvm::FunctionType *fty = llvm::FunctionType::get(retty, types, false); + IF_LOG Logger::cout() << "function type = " << *fty << '\n'; + + std::vector args; + args.insert(args.end(), outargs.begin(), outargs.end()); + args.insert(args.end(), inargs.begin(), inargs.end()); + + IF_LOG { + Logger::cout() << "Arguments:" << '\n'; + Logger::indent(); + size_t i = 0; + for (auto arg : args) { + Stream cout = Logger::cout(); + cout << '$' << i << " ==> " << *arg; + if (!llvm::isa(arg) && !llvm::isa(arg)) + cout << '\n'; + ++i; + } + Logger::undent(); + } + + llvm::InlineAsm *ia = llvm::InlineAsm::get(fty, code, out_c, true); + + llvm::CallInst *call = p->ir->CreateCall( + ia, args, retty == LLType::getVoidTy(gIR->context()) ? "" : "asm"); + + IF_LOG Logger::cout() << "Complete asm statement: " << *call << '\n'; + + // capture abi return value + if (useabiret) { + IRAsmBlock *block = p->asmBlock; + if (block->retfixup) + block->asmBlock->abiret = (*block->retfixup)(p->ir, call); + else if (p->asmBlock->retemu) + block->asmBlock->abiret = DtoLoad(block->asmBlock->abiret); else - retty = llvm::Type::getVoidTy(gIR->context()); + block->asmBlock->abiret = call; + } - // build argument types - std::vector types; - types.insert(types.end(), outtypes.begin(), outtypes.end()); - types.insert(types.end(), intypes.begin(), intypes.end()); - llvm::FunctionType* fty = llvm::FunctionType::get(retty, types, false); - IF_LOG Logger::cout() << "function type = " << *fty << '\n'; + p->asmBlock = NULL; - std::vector args; - args.insert(args.end(), outargs.begin(), outargs.end()); - args.insert(args.end(), inargs.begin(), inargs.end()); + // if asm contained external branches, emit goto forwarder code + if (!gotoToVal.empty()) { + assert(jump_target); - IF_LOG { - Logger::cout() << "Arguments:" << '\n'; - Logger::indent(); - size_t i = 0; - for (auto arg : args) { - Stream cout = Logger::cout(); - cout << '$' << i << " ==> " << *arg; - if (!llvm::isa(arg) && !llvm::isa(arg)) - cout << '\n'; - ++i; - } - Logger::undent(); + // make new blocks + llvm::BasicBlock *bb = llvm::BasicBlock::Create( + gIR->context(), "afterasmgotoforwarder", p->topfunc()); + + llvm::LoadInst *val = + p->ir->CreateLoad(jump_target, "__llvm_jump_target_value"); + llvm::SwitchInst *sw = p->ir->CreateSwitch(val, bb, gotoToVal.size()); + + // add all cases + for (const auto &pair : gotoToVal) { + llvm::BasicBlock *casebb = + llvm::BasicBlock::Create(gIR->context(), "case", p->topfunc(), bb); + sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), + pair.second), + casebb); + + p->scope() = IRScope(casebb); + DtoGoto(stmt->loc, pair.first); } - llvm::InlineAsm* ia = llvm::InlineAsm::get(fty, code, out_c, true); - - llvm::CallInst* call = p->ir->CreateCall(ia, args, - retty == LLType::getVoidTy(gIR->context()) ? "" : "asm"); - - IF_LOG Logger::cout() << "Complete asm statement: " << *call << '\n'; - - // capture abi return value - if (useabiret) - { - IRAsmBlock* block = p->asmBlock; - if (block->retfixup) - block->asmBlock->abiret = (*block->retfixup)(p->ir, call); - else if (p->asmBlock->retemu) - block->asmBlock->abiret = DtoLoad(block->asmBlock->abiret); - else - block->asmBlock->abiret = call; - } - - p->asmBlock = NULL; - - // if asm contained external branches, emit goto forwarder code - if(!gotoToVal.empty()) - { - assert(jump_target); - - // make new blocks - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterasmgotoforwarder", p->topfunc()); - - llvm::LoadInst* val = p->ir->CreateLoad(jump_target, "__llvm_jump_target_value"); - llvm::SwitchInst* sw = p->ir->CreateSwitch(val, bb, gotoToVal.size()); - - // add all cases - for (const auto& pair : gotoToVal) - { - llvm::BasicBlock* casebb = llvm::BasicBlock::Create(gIR->context(), "case", p->topfunc(), bb); - sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), pair.second), casebb); - - p->scope() = IRScope(casebb); - DtoGoto(stmt->loc, pair.first); - } - - p->scope() = IRScope(bb); - } + p->scope() = IRScope(bb); + } } ////////////////////////////////////////////////////////////////////////////// -CompoundAsmStatement* Statement::endsWithAsm() -{ - // does not end with inline asm - return NULL; +CompoundAsmStatement *Statement::endsWithAsm() { + // does not end with inline asm + return NULL; } -CompoundAsmStatement* CompoundStatement::endsWithAsm() -{ - // make the last inner statement decide - if (statements && statements->dim) - { - unsigned last = statements->dim - 1; - Statement* s = (*statements)[last]; - if (s) return s->endsWithAsm(); - } - return NULL; +CompoundAsmStatement *CompoundStatement::endsWithAsm() { + // make the last inner statement decide + if (statements && statements->dim) { + unsigned last = statements->dim - 1; + Statement *s = (*statements)[last]; + if (s) + return s->endsWithAsm(); + } + return NULL; } -CompoundAsmStatement* CompoundAsmStatement::endsWithAsm() -{ - // yes this is inline asm - return this; +CompoundAsmStatement *CompoundAsmStatement::endsWithAsm() { + // yes this is inline asm + return this; } ////////////////////////////////////////////////////////////////////////////// -void AsmStatement_toNakedIR(AsmStatement *stmt, IRState *irs) -{ - IF_LOG Logger::println("AsmStatement::toNakedIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; +void AsmStatement_toNakedIR(AsmStatement *stmt, IRState *irs) { + IF_LOG Logger::println("AsmStatement::toNakedIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; - // is there code? - if (!stmt->asmcode) - return; - AsmCode * code = static_cast(stmt->asmcode); + // is there code? + if (!stmt->asmcode) + return; + AsmCode *code = static_cast(stmt->asmcode); - // build asm stmt - replace_func_name(irs, code->insnTemplate); - irs->nakedAsm << "\t" << code->insnTemplate << std::endl; + // build asm stmt + replace_func_name(irs, code->insnTemplate); + irs->nakedAsm << "\t" << code->insnTemplate << std::endl; } diff --git a/gen/attributes.cpp b/gen/attributes.cpp index 9df6aa6bd2..e68e529593 100644 --- a/gen/attributes.cpp +++ b/gen/attributes.cpp @@ -10,63 +10,53 @@ #include "gen/attributes.h" #include "gen/irstate.h" -bool AttrBuilder::hasAttributes() const -{ - return builder.hasAttributes(); +bool AttrBuilder::hasAttributes() const { return builder.hasAttributes(); } + +bool AttrBuilder::contains(LLAttribute attribute) const { + return builder.contains(attribute); } -bool AttrBuilder::contains(LLAttribute attribute) const -{ - return builder.contains(attribute); +AttrBuilder &AttrBuilder::clear() { + builder.clear(); + return *this; } -AttrBuilder& AttrBuilder::clear() -{ - builder.clear(); - return *this; +AttrBuilder &AttrBuilder::add(LLAttribute attribute) { + // never set 'None' explicitly + if (attribute) + builder.addAttribute(attribute); + return *this; } -AttrBuilder& AttrBuilder::add(LLAttribute attribute) -{ - // never set 'None' explicitly - if (attribute) - builder.addAttribute(attribute); - return *this; +AttrBuilder &AttrBuilder::remove(LLAttribute attribute) { + // never remove 'None' explicitly + if (attribute) + builder.removeAttribute(attribute); + return *this; } -AttrBuilder& AttrBuilder::remove(LLAttribute attribute) -{ - // never remove 'None' explicitly - if (attribute) - builder.removeAttribute(attribute); - return *this; +AttrBuilder &AttrBuilder::merge(const AttrBuilder &other) { + builder.merge(other.builder); + return *this; } -AttrBuilder& AttrBuilder::merge(const AttrBuilder& other) -{ - builder.merge(other.builder); - return *this; +AttrSet +AttrSet::extractFunctionAndReturnAttributes(const llvm::Function *function) { + AttrSet r; + + llvm::AttributeSet old = function->getAttributes(); + llvm::AttributeSet existingAttrs[] = {old.getFnAttributes(), + old.getRetAttributes()}; + r.set = llvm::AttributeSet::get(gIR->context(), existingAttrs); + + return r; } - -AttrSet AttrSet::extractFunctionAndReturnAttributes(const llvm::Function* function) -{ - AttrSet r; - - llvm::AttributeSet old = function->getAttributes(); - llvm::AttributeSet existingAttrs[] = { old.getFnAttributes(), old.getRetAttributes() }; - r.set = llvm::AttributeSet::get(gIR->context(), existingAttrs); - - return r; -} - -AttrSet& AttrSet::add(unsigned index, const AttrBuilder& builder) -{ - if (builder.hasAttributes()) - { - llvm::AttributeSet as = llvm::AttributeSet::get( - gIR->context(), index, builder); - set = set.addAttributes(gIR->context(), index, as); - } - return *this; +AttrSet &AttrSet::add(unsigned index, const AttrBuilder &builder) { + if (builder.hasAttributes()) { + llvm::AttributeSet as = + llvm::AttributeSet::get(gIR->context(), index, builder); + set = set.addAttributes(gIR->context(), index, as); + } + return *this; } diff --git a/gen/attributes.h b/gen/attributes.h index 55e80284c6..26b5248a16 100644 --- a/gen/attributes.h +++ b/gen/attributes.h @@ -14,37 +14,36 @@ using LLAttribute = llvm::Attribute::AttrKind; -class AttrBuilder -{ - llvm::AttrBuilder builder; +class AttrBuilder { + llvm::AttrBuilder builder; public: - AttrBuilder() = default; + AttrBuilder() = default; - bool hasAttributes() const; - bool contains(LLAttribute attribute) const; + bool hasAttributes() const; + bool contains(LLAttribute attribute) const; - AttrBuilder& clear(); - AttrBuilder& add(LLAttribute attribute); - AttrBuilder& remove(LLAttribute attribute); - AttrBuilder& merge(const AttrBuilder& other); + AttrBuilder &clear(); + AttrBuilder &add(LLAttribute attribute); + AttrBuilder &remove(LLAttribute attribute); + AttrBuilder &merge(const AttrBuilder &other); - operator llvm::AttrBuilder&() { return builder; } - operator const llvm::AttrBuilder&() const { return builder; } + operator llvm::AttrBuilder &() { return builder; } + operator const llvm::AttrBuilder &() const { return builder; } }; -class AttrSet -{ - llvm::AttributeSet set; +class AttrSet { + llvm::AttributeSet set; public: - AttrSet() = default; - static AttrSet extractFunctionAndReturnAttributes(const llvm::Function* function); + AttrSet() = default; + static AttrSet + extractFunctionAndReturnAttributes(const llvm::Function *function); - AttrSet& add(unsigned index, const AttrBuilder& builder); + AttrSet &add(unsigned index, const AttrBuilder &builder); - operator llvm::AttributeSet&() { return set; } - operator const llvm::AttributeSet&() const { return set; } + operator llvm::AttributeSet &() { return set; } + operator const llvm::AttributeSet &() const { return set; } }; #endif diff --git a/gen/binops.cpp b/gen/binops.cpp index f3a6173969..7ce61d6bd2 100644 --- a/gen/binops.cpp +++ b/gen/binops.cpp @@ -18,140 +18,131 @@ ////////////////////////////////////////////////////////////////////////////// -DValue* DtoBinAdd(DValue* lhs, DValue* rhs) -{ - Type* t = lhs->getType(); - LLValue *l, *r; - l = lhs->getRVal(); - r = rhs->getRVal(); +DValue *DtoBinAdd(DValue *lhs, DValue *rhs) { + Type *t = lhs->getType(); + LLValue *l, *r; + l = lhs->getRVal(); + r = rhs->getRVal(); - LLValue* res; - if (t->isfloating()) - res = gIR->ir->CreateFAdd(l, r); + LLValue *res; + if (t->isfloating()) + res = gIR->ir->CreateFAdd(l, r); + else + res = gIR->ir->CreateAdd(l, r); + + return new DImValue(t, res); +} + +////////////////////////////////////////////////////////////////////////////// + +DValue *DtoBinSub(DValue *lhs, DValue *rhs) { + Type *t = lhs->getType(); + LLValue *l, *r; + l = lhs->getRVal(); + r = rhs->getRVal(); + + LLValue *res; + if (t->isfloating()) + res = gIR->ir->CreateFSub(l, r); + else + res = gIR->ir->CreateSub(l, r); + + return new DImValue(t, res); +} + +////////////////////////////////////////////////////////////////////////////// + +DValue *DtoBinMul(Type *targettype, DValue *lhs, DValue *rhs) { + Type *t = lhs->getType(); + LLValue *l, *r; + l = lhs->getRVal(); + r = rhs->getRVal(); + + LLValue *res; + if (t->isfloating()) + res = gIR->ir->CreateFMul(l, r); + else + res = gIR->ir->CreateMul(l, r); + return new DImValue(targettype, res); +} + +////////////////////////////////////////////////////////////////////////////// + +DValue *DtoBinDiv(Type *targettype, DValue *lhs, DValue *rhs) { + Type *t = lhs->getType(); + LLValue *l, *r; + l = lhs->getRVal(); + r = rhs->getRVal(); + + LLValue *res; + if (t->isfloating()) + res = gIR->ir->CreateFDiv(l, r); + else if (!isLLVMUnsigned(t)) + res = gIR->ir->CreateSDiv(l, r); + else + res = gIR->ir->CreateUDiv(l, r); + return new DImValue(targettype, res); +} + +////////////////////////////////////////////////////////////////////////////// + +DValue *DtoBinRem(Type *targettype, DValue *lhs, DValue *rhs) { + Type *t = lhs->getType(); + LLValue *l, *r; + l = lhs->getRVal(); + r = rhs->getRVal(); + LLValue *res; + if (t->isfloating()) + res = gIR->ir->CreateFRem(l, r); + else if (!isLLVMUnsigned(t)) + res = gIR->ir->CreateSRem(l, r); + else + res = gIR->ir->CreateURem(l, r); + return new DImValue(targettype, res); +} + +////////////////////////////////////////////////////////////////////////////// + +LLValue *DtoBinNumericEquals(Loc &loc, DValue *lhs, DValue *rhs, TOK op) { + assert(op == TOKequal || op == TOKnotequal || op == TOKidentity || + op == TOKnotidentity); + Type *t = lhs->getType()->toBasetype(); + assert(t->isfloating()); + Logger::println("numeric equality"); + + LLValue *res = 0; + if (t->iscomplex()) { + Logger::println("complex"); + res = DtoComplexEquals(loc, op, lhs, rhs); + } else if (t->isfloating()) { + Logger::println("floating"); + res = DtoBinFloatsEquals(loc, lhs, rhs, op); + } + + assert(res); + return res; +} + +////////////////////////////////////////////////////////////////////////////// + +LLValue *DtoBinFloatsEquals(Loc &loc, DValue *lhs, DValue *rhs, TOK op) { + LLValue *res = 0; + if (op == TOKequal) { + res = gIR->ir->CreateFCmpOEQ(lhs->getRVal(), rhs->getRVal()); + } else if (op == TOKnotequal) { + res = gIR->ir->CreateFCmpUNE(lhs->getRVal(), rhs->getRVal()); + } else { + llvm::ICmpInst::Predicate cmpop; + if (op == TOKidentity) + cmpop = llvm::ICmpInst::ICMP_EQ; else - res = gIR->ir->CreateAdd(l, r); + cmpop = llvm::ICmpInst::ICMP_NE; - return new DImValue( t, res ); -} - -////////////////////////////////////////////////////////////////////////////// - -DValue* DtoBinSub(DValue* lhs, DValue* rhs) -{ - Type* t = lhs->getType(); - LLValue *l, *r; - l = lhs->getRVal(); - r = rhs->getRVal(); - - LLValue* res; - if (t->isfloating()) - res = gIR->ir->CreateFSub(l, r); - else - res = gIR->ir->CreateSub(l, r); - - return new DImValue( t, res ); -} - -////////////////////////////////////////////////////////////////////////////// - -DValue* DtoBinMul(Type* targettype, DValue* lhs, DValue* rhs) -{ - Type* t = lhs->getType(); - LLValue *l, *r; - l = lhs->getRVal(); - r = rhs->getRVal(); - - LLValue* res; - if (t->isfloating()) - res = gIR->ir->CreateFMul(l, r); - else - res = gIR->ir->CreateMul(l, r); - return new DImValue( targettype, res ); -} - -////////////////////////////////////////////////////////////////////////////// - -DValue* DtoBinDiv(Type* targettype, DValue* lhs, DValue* rhs) -{ - Type* t = lhs->getType(); - LLValue *l, *r; - l = lhs->getRVal(); - r = rhs->getRVal(); - - LLValue* res; - if (t->isfloating()) - res = gIR->ir->CreateFDiv(l, r); - else if (!isLLVMUnsigned(t)) - res = gIR->ir->CreateSDiv(l, r); - else - res = gIR->ir->CreateUDiv(l, r); - return new DImValue( targettype, res ); -} - -////////////////////////////////////////////////////////////////////////////// - -DValue* DtoBinRem(Type* targettype, DValue* lhs, DValue* rhs) -{ - Type* t = lhs->getType(); - LLValue *l, *r; - l = lhs->getRVal(); - r = rhs->getRVal(); - LLValue* res; - if (t->isfloating()) - res = gIR->ir->CreateFRem(l, r); - else if (!isLLVMUnsigned(t)) - res = gIR->ir->CreateSRem(l, r); - else - res = gIR->ir->CreateURem(l, r); - return new DImValue( targettype, res ); -} - -////////////////////////////////////////////////////////////////////////////// - -LLValue* DtoBinNumericEquals(Loc& loc, DValue* lhs, DValue* rhs, TOK op) -{ - assert(op == TOKequal || op == TOKnotequal || - op == TOKidentity || op == TOKnotidentity); - Type* t = lhs->getType()->toBasetype(); - assert(t->isfloating()); - Logger::println("numeric equality"); - - LLValue* res = 0; - if (t->iscomplex()) - { - Logger::println("complex"); - res = DtoComplexEquals(loc, op, lhs, rhs); - } - else if (t->isfloating()) - { - Logger::println("floating"); - res = DtoBinFloatsEquals(loc, lhs, rhs, op); - } - - assert(res); - return res; -} - -////////////////////////////////////////////////////////////////////////////// - -LLValue* DtoBinFloatsEquals(Loc& loc, DValue* lhs, DValue* rhs, TOK op) -{ - LLValue* res = 0; - if (op == TOKequal) { - res = gIR->ir->CreateFCmpOEQ(lhs->getRVal(), rhs->getRVal()); - } else if (op == TOKnotequal) { - res = gIR->ir->CreateFCmpUNE(lhs->getRVal(), rhs->getRVal()); - } else { - llvm::ICmpInst::Predicate cmpop; - if (op == TOKidentity) - cmpop = llvm::ICmpInst::ICMP_EQ; - else - cmpop = llvm::ICmpInst::ICMP_NE; - - LLValue* sz = DtoConstSize_t(getTypeStoreSize(DtoType(lhs->getType()))); - LLValue* val = DtoMemCmp(makeLValue(loc, lhs), makeLValue(loc, rhs), sz); - res = gIR->ir->CreateICmp(cmpop, val, LLConstantInt::get(val->getType(), 0, false)); - } - assert(res); - return res; + LLValue *sz = DtoConstSize_t(getTypeStoreSize(DtoType(lhs->getType()))); + LLValue *val = DtoMemCmp(makeLValue(loc, lhs), makeLValue(loc, rhs), sz); + res = gIR->ir->CreateICmp(cmpop, val, + LLConstantInt::get(val->getType(), 0, false)); + } + assert(res); + return res; } diff --git a/gen/cl_helpers.cpp b/gen/cl_helpers.cpp index 2518583d6d..b1454be8dc 100644 --- a/gen/cl_helpers.cpp +++ b/gen/cl_helpers.cpp @@ -12,39 +12,38 @@ #include "rmem.h" #include "root.h" #include -#include // isupper, tolower +#include // isupper, tolower #include #include namespace opts { -MultiSetter::MultiSetter(bool invert, bool* p, ...) { - this->invert = invert; - if (p) { - locations.push_back(p); - va_list va; - va_start(va, p); - while ((p = va_arg(va, bool*))) { - locations.push_back(p); - } - va_end(va); +MultiSetter::MultiSetter(bool invert, bool *p, ...) { + this->invert = invert; + if (p) { + locations.push_back(p); + va_list va; + va_start(va, p); + while ((p = va_arg(va, bool *))) { + locations.push_back(p); } + va_end(va); + } } void MultiSetter::operator=(bool val) { - for (auto& l : locations) { - *l = (val != invert); - } + for (auto &l : locations) { + *l = (val != invert); + } } +void StringsAdapter::push_back(const char *cstr) { + if (!cstr || !*cstr) + error(Loc(), "Expected argument to '-%s'", name); -void StringsAdapter::push_back(const char* cstr) { - if (!cstr || !*cstr) - error(Loc(), "Expected argument to '-%s'", name); - - if (!*arrp) - *arrp = new Strings; - (*arrp)->push(mem.xstrdup(cstr)); + if (!*arrp) + *arrp = new Strings; + (*arrp)->push(mem.xstrdup(cstr)); } } // namespace opts diff --git a/gen/cl_helpers.h b/gen/cl_helpers.h index 931d025c7c..060b2079bd 100644 --- a/gen/cl_helpers.h +++ b/gen/cl_helpers.h @@ -27,163 +27,161 @@ template struct Array; typedef Array Strings; namespace opts { - namespace cl = llvm::cl; +namespace cl = llvm::cl; - /// Helper class to determine values - template - struct FlagParserDataType {}; +/// Helper class to determine values +template struct FlagParserDataType {}; - template<> - struct FlagParserDataType{ - static bool true_val() { return true; } - static bool false_val() { return false; } - }; +template <> struct FlagParserDataType { + static bool true_val() { return true; } + static bool false_val() { return false; } +}; - template<> - struct FlagParserDataType { - static cl::boolOrDefault true_val() { return cl::BOU_TRUE; } - static cl::boolOrDefault false_val() { return cl::BOU_FALSE; } - }; +template <> struct FlagParserDataType { + static cl::boolOrDefault true_val() { return cl::BOU_TRUE; } + static cl::boolOrDefault false_val() { return cl::BOU_FALSE; } +}; - template - class FlagParser : public cl::generic_parser_base { - protected: - llvm::SmallVector, 2> switches; +template class FlagParser : public cl::generic_parser_base { +protected: + llvm::SmallVector, 2> switches; #if LDC_LLVM_VER >= 307 - cl::Option& owner() const { return Owner; } + cl::Option &owner() const { return Owner; } #else - cl::Option* Owner; - cl::Option& owner() const { return *Owner; } + cl::Option *Owner; + cl::Option &owner() const { return *Owner; } #endif - public: +public: #if LDC_LLVM_VER >= 307 - FlagParser(cl::Option& O) : generic_parser_base(O) {} + FlagParser(cl::Option &O) : generic_parser_base(O) {} #else - FlagParser() : generic_parser_base(), Owner(0) {} + FlagParser() : generic_parser_base(), Owner(0) {} #endif - typedef DataType parser_data_type; + typedef DataType parser_data_type; #if LDC_LLVM_VER >= 307 - void initialize() { + void initialize() { #else - void initialize(cl::Option& O) { - Owner = &O; + void initialize(cl::Option &O) { + Owner = &O; #endif - std::string Name(owner().ArgStr); - switches.push_back(make_pair("enable-" + Name, FlagParserDataType::true_val())); - switches.push_back(make_pair("disable-" + Name, FlagParserDataType::false_val())); - // Replace with -enable- and register -disable- + std::string Name(owner().ArgStr); + switches.push_back( + make_pair("enable-" + Name, FlagParserDataType::true_val())); + switches.push_back(make_pair("disable-" + Name, + FlagParserDataType::false_val())); +// Replace with -enable- and register -disable- #if LDC_LLVM_VER >= 307 - // A literal option can only registered if the argstr is empty - - // just do this first. - owner().setArgStr(""); - AddLiteralOption(Owner, strdup(switches[1].first.data())); + // A literal option can only registered if the argstr is empty - + // just do this first. + owner().setArgStr(""); + AddLiteralOption(Owner, strdup(switches[1].first.data())); #endif - owner().setArgStr(switches[0].first.data()); + owner().setArgStr(switches[0].first.data()); + } + + enum cl::ValueExpected getValueExpectedFlagDefault() const { + return cl::ValueOptional; + } + + // Implement virtual functions needed by generic_parser_base + unsigned getNumOptions() const LLVM_OVERRIDE { return 1; } + const char *getOption(unsigned N) const LLVM_OVERRIDE { + assert(N == 0); + return owner().ArgStr; + } + + const char *getDescription(unsigned N) const LLVM_OVERRIDE { + assert(N == 0); + return owner().HelpStr; + } + +private: + struct OptionValue : cl::OptionValueBase { + OptionValue(){}; + }; + const OptionValue EmptyOptionValue; + +public: + // getOptionValue - Return the value of option name N. + const cl::GenericOptionValue &getOptionValue(unsigned N) const LLVM_OVERRIDE { + return EmptyOptionValue; + } + + // parse - Return true on error. + bool parse(cl::Option &O, llvm::StringRef ArgName, llvm::StringRef Arg, + DataType &Val) { + for (const auto &pair : switches) { + const auto &name = pair.first; + if (name == ArgName || (name.size() < ArgName.size() && + ArgName.substr(0, name.size()) == name && + ArgName[name.size()] == '=')) { + if (!parse(owner(), Arg, Val)) { + Val = (Val == pair.second) + ? FlagParserDataType::true_val() + : FlagParserDataType::false_val(); + return false; } + // Invalid option value + break; + } + } + return true; + } - enum cl::ValueExpected getValueExpectedFlagDefault() const { - return cl::ValueOptional; - } + void getExtraOptionNames(llvm::SmallVectorImpl &Names) { + for (auto I = switches.begin() + 1, E = switches.end(); I != E; ++I) { + Names.push_back(I->first.data()); + } + } - // Implement virtual functions needed by generic_parser_base - unsigned getNumOptions() const LLVM_OVERRIDE { return 1; } - const char* getOption(unsigned N) const LLVM_OVERRIDE { - assert(N == 0); - return owner().ArgStr; - } +private: + static bool parse(cl::Option &O, llvm::StringRef Arg, DataType &Val) { + if (Arg == "" || Arg == "true" || Arg == "TRUE" || Arg == "True" || + Arg == "1") { + Val = FlagParserDataType::true_val(); + return false; + } - const char* getDescription(unsigned N) const LLVM_OVERRIDE { - assert(N == 0); - return owner().HelpStr; - } + if (Arg == "false" || Arg == "FALSE" || Arg == "False" || Arg == "0") { + Val = FlagParserDataType::false_val(); + return false; + } + return O.error("'" + Arg + + "' is invalid value for boolean argument! Try 0 or 1"); + } +}; - private: - struct OptionValue : cl::OptionValueBase < DataType, false > { - OptionValue() { }; - }; - const OptionValue EmptyOptionValue; +/// Helper class for options that set multiple flags +class MultiSetter { + std::vector locations; + bool invert; + MultiSetter(bool); // not implemented, disable auto-conversion +public: + MultiSetter(bool invert, bool *p, ...) LLVM_END_WITH_NULL; - public: - // getOptionValue - Return the value of option name N. - const cl::GenericOptionValue &getOptionValue(unsigned N) const LLVM_OVERRIDE { - return EmptyOptionValue; - } + void operator=(bool val); +}; - // parse - Return true on error. - bool parse(cl::Option& O, llvm::StringRef ArgName, llvm::StringRef Arg, DataType& Val) { - for (const auto& pair : switches) { - const auto& name = pair.first; - if (name == ArgName - || (name.size() < ArgName.size() - && ArgName.substr(0, name.size()) == name - && ArgName[name.size()] == '=')) { - if (!parse(owner(), Arg, Val)) - { - Val = (Val == pair.second) ? FlagParserDataType::true_val() : FlagParserDataType::false_val(); - return false; - } - // Invalid option value - break; - } - } - return true; - } +/// Helper class to fill Strings with char* when given strings +/// (Errors on empty strings) +class StringsAdapter { + const char *name; + Strings **arrp; - void getExtraOptionNames(llvm::SmallVectorImpl& Names) { - for (auto I = switches.begin() + 1, E = switches.end(); I != E; ++I) { - Names.push_back(I->first.data()); - } - } +public: + StringsAdapter(const char *name_, Strings *&arr) { + name = name_; + arrp = &arr; + assert(name); + assert(arrp); + } - private: - static bool parse(cl::Option &O, llvm::StringRef Arg, DataType &Val) { - if (Arg == "" || Arg == "true" || Arg == "TRUE" || Arg == "True" || - Arg == "1") { - Val = FlagParserDataType::true_val(); - return false; - } + void push_back(const char *cstr); - if (Arg == "false" || Arg == "FALSE" || Arg == "False" || Arg == "0") { - Val = FlagParserDataType::false_val(); - return false; - } - return O.error("'" + Arg + - "' is invalid value for boolean argument! Try 0 or 1"); - } - }; - - /// Helper class for options that set multiple flags - class MultiSetter { - std::vector locations; - bool invert; - MultiSetter(bool); //not implemented, disable auto-conversion - public: - MultiSetter(bool invert, bool* p, ...) LLVM_END_WITH_NULL; - - void operator=(bool val); - }; - - /// Helper class to fill Strings with char* when given strings - /// (Errors on empty strings) - class StringsAdapter { - const char* name; - Strings** arrp; - public: - StringsAdapter(const char* name_, Strings*& arr) { - name = name_; - arrp = &arr; - assert(name); - assert(arrp); - } - - void push_back(const char* cstr); - - void push_back(const std::string& str) { - push_back(str.c_str()); - } - }; + void push_back(const std::string &str) { push_back(str.c_str()); } +}; } #endif diff --git a/gen/classes.cpp b/gen/classes.cpp index 006d21dbcf..3bd95d0219 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -33,403 +33,403 @@ // FIXME: this needs to be cleaned up -void DtoResolveClass(ClassDeclaration* cd) -{ - if (cd->ir.isResolved()) return; - cd->ir.setResolved(); +void DtoResolveClass(ClassDeclaration *cd) { + if (cd->ir.isResolved()) + return; + cd->ir.setResolved(); - IF_LOG Logger::println("DtoResolveClass(%s): %s", cd->toPrettyChars(), cd->loc.toChars()); + IF_LOG Logger::println("DtoResolveClass(%s): %s", cd->toPrettyChars(), + cd->loc.toChars()); + LOG_SCOPE; + + // make sure the base classes are processed first + for (auto bc : *cd->baseclasses) { + DtoResolveClass(bc->base); + } + + // make sure type exists + DtoType(cd->type); + + // create IrAggr + IrAggr *irAggr = getIrAggr(cd, true); + + // make sure all fields really get their ir field + for (auto vd : cd->fields) { + IF_LOG { + if (isIrFieldCreated(vd)) + Logger::println("class field already exists"); + } + getIrField(vd, true); + } + + // emit the interfaceInfosZ symbol if necessary + if (cd->vtblInterfaces && cd->vtblInterfaces->dim > 0) + irAggr->getInterfaceArraySymbol(); // initializer is applied when it's built + + // interface only emit typeinfo and classinfo + if (cd->isInterfaceDeclaration()) { + irAggr->initializeInterface(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DValue *DtoNewClass(Loc &loc, TypeClass *tc, NewExp *newexp) { + // resolve type + DtoResolveClass(tc->sym); + + // allocate + LLValue *mem; + if (newexp->onstack) { + // FIXME align scope class to its largest member + mem = DtoRawAlloca(DtoType(tc)->getContainedType(0), 0, ".newclass_alloca"); + } + // custom allocator + else if (newexp->allocator) { + DtoResolveFunction(newexp->allocator); + DFuncValue dfn(newexp->allocator, getIrFunc(newexp->allocator)->func); + DValue *res = DtoCallFunction(newexp->loc, NULL, &dfn, newexp->newargs); + mem = DtoBitCast(res->getRVal(), DtoType(tc), ".newclass_custom"); + } + // default allocator + else { + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_newclass"); + LLConstant *ci = DtoBitCast(getIrAggr(tc->sym)->getClassInfoSymbol(), + DtoType(Type::typeinfoclass->type)); + mem = + gIR->CreateCallOrInvoke(fn, ci, ".newclass_gc_alloc").getInstruction(); + mem = DtoBitCast(mem, DtoType(tc), ".newclass_gc"); + } + + // init + DtoInitClass(tc, mem); + + // init inner-class outer reference + if (newexp->thisexp) { + Logger::println("Resolving outer class"); LOG_SCOPE; + DValue *thisval = toElem(newexp->thisexp); + unsigned idx = getFieldGEPIndex(tc->sym, tc->sym->vthis); + LLValue *src = thisval->getRVal(); + LLValue *dst = DtoGEPi(mem, 0, idx); + IF_LOG Logger::cout() << "dst: " << *dst << "\nsrc: " << *src << '\n'; + DtoStore(src, DtoBitCast(dst, getPtrToType(src->getType()))); + } + // set the context for nested classes + else if (tc->sym->isNested() && tc->sym->vthis) { + DtoResolveNestedContext(loc, tc->sym, mem); + } - // make sure the base classes are processed first - for (auto bc : *cd->baseclasses) - { - DtoResolveClass(bc->base); - } + // call constructor + if (newexp->member) { + // evaluate argprefix + if (newexp->argprefix) + toElemDtor(newexp->argprefix); - // make sure type exists - DtoType(cd->type); + Logger::println("Calling constructor"); + assert(newexp->arguments != NULL); + DtoResolveFunction(newexp->member); + DFuncValue dfn(newexp->member, getIrFunc(newexp->member)->func, mem); + return DtoCallFunction(newexp->loc, tc, &dfn, newexp->arguments); + } - // create IrAggr - IrAggr* irAggr = getIrAggr(cd, true); + assert(newexp->argprefix == NULL); - // make sure all fields really get their ir field - for (auto vd : cd->fields) - { - IF_LOG { - if (isIrFieldCreated(vd)) - Logger::println("class field already exists"); - } - getIrField(vd, true); - } - - // emit the interfaceInfosZ symbol if necessary - if (cd->vtblInterfaces && cd->vtblInterfaces->dim > 0) - irAggr->getInterfaceArraySymbol(); // initializer is applied when it's built - - // interface only emit typeinfo and classinfo - if (cd->isInterfaceDeclaration()) - { - irAggr->initializeInterface(); - } + // return default constructed class + return new DImValue(tc, mem); } ////////////////////////////////////////////////////////////////////////////////////////// -DValue* DtoNewClass(Loc& loc, TypeClass* tc, NewExp* newexp) -{ - // resolve type - DtoResolveClass(tc->sym); +void DtoInitClass(TypeClass *tc, LLValue *dst) { + DtoResolveClass(tc->sym); - // allocate - LLValue* mem; - if (newexp->onstack) - { - // FIXME align scope class to its largest member - mem = DtoRawAlloca(DtoType(tc)->getContainedType(0), 0, ".newclass_alloca"); - } - // custom allocator - else if (newexp->allocator) - { - DtoResolveFunction(newexp->allocator); - DFuncValue dfn(newexp->allocator, getIrFunc(newexp->allocator)->func); - DValue* res = DtoCallFunction(newexp->loc, NULL, &dfn, newexp->newargs); - mem = DtoBitCast(res->getRVal(), DtoType(tc), ".newclass_custom"); - } - // default allocator - else - { - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_newclass"); - LLConstant* ci = DtoBitCast(getIrAggr(tc->sym)->getClassInfoSymbol(), DtoType(Type::typeinfoclass->type)); - mem = gIR->CreateCallOrInvoke(fn, ci, ".newclass_gc_alloc").getInstruction(); - mem = DtoBitCast(mem, DtoType(tc), ".newclass_gc"); - } + // Set vtable field. Doing this seperately might be optimized better. + LLValue *tmp = DtoGEPi(dst, 0, 0, "vtbl"); + LLValue *val = DtoBitCast(getIrAggr(tc->sym)->getVtblSymbol(), + tmp->getType()->getContainedType(0)); + DtoStore(val, tmp); - // init - DtoInitClass(tc, mem); - - // init inner-class outer reference - if (newexp->thisexp) - { - Logger::println("Resolving outer class"); - LOG_SCOPE; - DValue* thisval = toElem(newexp->thisexp); - unsigned idx = getFieldGEPIndex(tc->sym, tc->sym->vthis); - LLValue* src = thisval->getRVal(); - LLValue* dst = DtoGEPi(mem, 0, idx); - IF_LOG Logger::cout() << "dst: " << *dst << "\nsrc: " << *src << '\n'; - DtoStore(src, DtoBitCast(dst, getPtrToType(src->getType()))); - } - // set the context for nested classes - else if (tc->sym->isNested() && tc->sym->vthis) - { - DtoResolveNestedContext(loc, tc->sym, mem); - } - - // call constructor - if (newexp->member) - { - // evaluate argprefix - if (newexp->argprefix) - toElemDtor(newexp->argprefix); - - Logger::println("Calling constructor"); - assert(newexp->arguments != NULL); - DtoResolveFunction(newexp->member); - DFuncValue dfn(newexp->member, getIrFunc(newexp->member)->func, mem); - return DtoCallFunction(newexp->loc, tc, &dfn, newexp->arguments); - } - - assert(newexp->argprefix == NULL); - - // return default constructed class - return new DImValue(tc, mem); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -void DtoInitClass(TypeClass* tc, LLValue* dst) -{ - DtoResolveClass(tc->sym); - - // Set vtable field. Doing this seperately might be optimized better. - LLValue* tmp = DtoGEPi(dst, 0, 0, "vtbl"); - LLValue* val = DtoBitCast(getIrAggr(tc->sym)->getVtblSymbol(), - tmp->getType()->getContainedType(0)); + // For D classes, set the monitor field to null. + const bool isCPPclass = tc->sym->isCPPclass() ? true : false; + if (!isCPPclass) { + tmp = DtoGEPi(dst, 0, 1, "monitor"); + val = LLConstant::getNullValue(tmp->getType()->getContainedType(0)); DtoStore(val, tmp); + } - // For D classes, set the monitor field to null. - const bool isCPPclass = tc->sym->isCPPclass() ? true : false; - if (!isCPPclass) - { - tmp = DtoGEPi(dst, 0, 1, "monitor"); - val = LLConstant::getNullValue(tmp->getType()->getContainedType(0)); - DtoStore(val, tmp); - } + // Copy the rest from the static initializer, if any. + unsigned const firstDataIdx = isCPPclass ? 1 : 2; + uint64_t const dataBytes = + tc->sym->structsize - Target::ptrsize * firstDataIdx; + if (dataBytes == 0) + return; - // Copy the rest from the static initializer, if any. - unsigned const firstDataIdx = isCPPclass ? 1 : 2; - uint64_t const dataBytes = tc->sym->structsize - Target::ptrsize * firstDataIdx; - if (dataBytes == 0) - return; + LLValue *dstarr = DtoGEPi(dst, 0, firstDataIdx); - LLValue* dstarr = DtoGEPi(dst, 0, firstDataIdx); + // init symbols might not have valid types + LLValue *initsym = getIrAggr(tc->sym)->getInitSymbol(); + initsym = DtoBitCast(initsym, DtoType(tc)); + LLValue *srcarr = DtoGEPi(initsym, 0, firstDataIdx); - // init symbols might not have valid types - LLValue* initsym = getIrAggr(tc->sym)->getInitSymbol(); - initsym = DtoBitCast(initsym, DtoType(tc)); - LLValue* srcarr = DtoGEPi(initsym, 0, firstDataIdx); - - DtoMemCpy(dstarr, srcarr, DtoConstSize_t(dataBytes)); + DtoMemCpy(dstarr, srcarr, DtoConstSize_t(dataBytes)); } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoFinalizeClass(Loc& loc, LLValue* inst) -{ - // get runtime function - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_callfinalizer"); +void DtoFinalizeClass(Loc &loc, LLValue *inst) { + // get runtime function + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_callfinalizer"); - gIR->CreateCallOrInvoke(fn, - DtoBitCast(inst, fn->getFunctionType()->getParamType(0)), ""); + gIR->CreateCallOrInvoke( + fn, DtoBitCast(inst, fn->getFunctionType()->getParamType(0)), ""); } ////////////////////////////////////////////////////////////////////////////////////////// -DValue* DtoCastClass(Loc& loc, DValue* val, Type* _to) -{ - IF_LOG Logger::println("DtoCastClass(%s, %s)", val->getType()->toChars(), _to->toChars()); - LOG_SCOPE; +DValue *DtoCastClass(Loc &loc, DValue *val, Type *_to) { + IF_LOG Logger::println("DtoCastClass(%s, %s)", val->getType()->toChars(), + _to->toChars()); + LOG_SCOPE; - Type* to = _to->toBasetype(); + Type *to = _to->toBasetype(); - // class -> pointer - if (to->ty == Tpointer) { - IF_LOG Logger::println("to pointer"); - LLType* tolltype = DtoType(_to); - LLValue* rval = DtoBitCast(val->getRVal(), tolltype); - return new DImValue(_to, rval); + // class -> pointer + if (to->ty == Tpointer) { + IF_LOG Logger::println("to pointer"); + LLType *tolltype = DtoType(_to); + LLValue *rval = DtoBitCast(val->getRVal(), tolltype); + return new DImValue(_to, rval); + } + // class -> bool + else if (to->ty == Tbool) { + IF_LOG Logger::println("to bool"); + LLValue *llval = val->getRVal(); + LLValue *zero = LLConstant::getNullValue(llval->getType()); + return new DImValue(_to, gIR->ir->CreateICmpNE(llval, zero)); + } + // class -> integer + else if (to->isintegral()) { + IF_LOG Logger::println("to %s", to->toChars()); + + // get class ptr + LLValue *v = val->getRVal(); + // cast to size_t + v = gIR->ir->CreatePtrToInt(v, DtoSize_t(), ""); + // cast to the final int type + DImValue im(Type::tsize_t, v); + return DtoCastInt(loc, &im, _to); + } + // class -> typeof(null) + else if (to->ty == Tnull) { + IF_LOG Logger::println("to %s", to->toChars()); + return new DImValue(_to, LLConstant::getNullValue(DtoType(_to))); + } + + // must be class/interface + assert(to->ty == Tclass); + TypeClass *tc = static_cast(to); + + // from type + Type *from = val->getType()->toBasetype(); + TypeClass *fc = static_cast(from); + + if (fc->sym->isCPPclass()) { + IF_LOG Logger::println("C++ class/interface, just bitcasting"); + LLValue *rval = DtoBitCast(val->getRVal(), DtoType(_to)); + return new DImValue(_to, rval); + } + + // x -> interface + if (InterfaceDeclaration *it = tc->sym->isInterfaceDeclaration()) { + Logger::println("to interface"); + // interface -> interface + if (fc->sym->isInterfaceDeclaration()) { + Logger::println("from interface"); + return DtoDynamicCastInterface(loc, val, _to); } - // class -> bool - else if (to->ty == Tbool) { - IF_LOG Logger::println("to bool"); - LLValue* llval = val->getRVal(); - LLValue* zero = LLConstant::getNullValue(llval->getType()); - return new DImValue(_to, gIR->ir->CreateICmpNE(llval, zero)); + // class -> interface - static cast + else if (it->isBaseOf(fc->sym, NULL)) { + Logger::println("static down cast"); + + // get the from class + ClassDeclaration *cd = fc->sym->isClassDeclaration(); + DtoResolveClass(cd); // add this + IrTypeClass *typeclass = stripModifiers(fc)->ctype->isClass(); + + // find interface impl + + size_t i_index = typeclass->getInterfaceIndex(it); + assert(i_index != ~0UL && + "requesting interface that is not implemented by this class"); + + // offset pointer + LLValue *v = val->getRVal(); + LLValue *orig = v; + v = DtoGEPi(v, 0, i_index); + LLType *ifType = DtoType(_to); + IF_LOG { + Logger::cout() << "V = " << *v << std::endl; + Logger::cout() << "T = " << *ifType << std::endl; + } + v = DtoBitCast(v, ifType); + + // Check whether the original value was null, and return null if so. + // Sure we could have jumped over the code above in this case, but + // it's just a GEP and (maybe) a pointer-to-pointer BitCast, so it + // should be pretty cheap and perfectly safe even if the original was + // null. + LLValue *isNull = gIR->ir->CreateICmpEQ( + orig, LLConstant::getNullValue(orig->getType()), ".nullcheck"); + v = gIR->ir->CreateSelect(isNull, LLConstant::getNullValue(ifType), v, + ".interface"); + + // return r-value + return new DImValue(_to, v); } - // class -> integer - else if (to->isintegral()) { - IF_LOG Logger::println("to %s", to->toChars()); - - // get class ptr - LLValue* v = val->getRVal(); - // cast to size_t - v = gIR->ir->CreatePtrToInt(v, DtoSize_t(), ""); - // cast to the final int type - DImValue im(Type::tsize_t, v); - return DtoCastInt(loc, &im, _to); - } - // class -> typeof(null) - else if (to->ty == Tnull) { - IF_LOG Logger::println("to %s", to->toChars()); - return new DImValue(_to, LLConstant::getNullValue(DtoType(_to))); - } - - // must be class/interface - assert(to->ty == Tclass); - TypeClass* tc = static_cast(to); - - // from type - Type* from = val->getType()->toBasetype(); - TypeClass* fc = static_cast(from); - - if (fc->sym->isCPPclass()) { - IF_LOG Logger::println("C++ class/interface, just bitcasting"); - LLValue* rval = DtoBitCast(val->getRVal(), DtoType(_to)); - return new DImValue(_to, rval); - } - - // x -> interface - if (InterfaceDeclaration* it = tc->sym->isInterfaceDeclaration()) { - Logger::println("to interface"); - // interface -> interface - if (fc->sym->isInterfaceDeclaration()) { - Logger::println("from interface"); - return DtoDynamicCastInterface(loc, val, _to); - } - // class -> interface - static cast - else if (it->isBaseOf(fc->sym,NULL)) { - Logger::println("static down cast"); - - // get the from class - ClassDeclaration* cd = fc->sym->isClassDeclaration(); - DtoResolveClass(cd); // add this - IrTypeClass* typeclass = stripModifiers(fc)->ctype->isClass(); - - // find interface impl - - size_t i_index = typeclass->getInterfaceIndex(it); - assert(i_index != ~0UL && "requesting interface that is not implemented by this class"); - - // offset pointer - LLValue* v = val->getRVal(); - LLValue* orig = v; - v = DtoGEPi(v, 0, i_index); - LLType* ifType = DtoType(_to); - IF_LOG { - Logger::cout() << "V = " << *v << std::endl; - Logger::cout() << "T = " << *ifType << std::endl; - } - v = DtoBitCast(v, ifType); - - // Check whether the original value was null, and return null if so. - // Sure we could have jumped over the code above in this case, but - // it's just a GEP and (maybe) a pointer-to-pointer BitCast, so it - // should be pretty cheap and perfectly safe even if the original was null. - LLValue* isNull = gIR->ir->CreateICmpEQ(orig, LLConstant::getNullValue(orig->getType()), ".nullcheck"); - v = gIR->ir->CreateSelect(isNull, LLConstant::getNullValue(ifType), v, ".interface"); - - // return r-value - return new DImValue(_to, v); - } - // class -> interface - else { - Logger::println("from object"); - return DtoDynamicCastObject(loc, val, _to); - } - } - // x -> class + // class -> interface else { - Logger::println("to class"); - // interface -> class - if (fc->sym->isInterfaceDeclaration()) { - Logger::println("interface cast"); - return DtoDynamicCastInterface(loc, val, _to); - } - // class -> class - static down cast - else if (tc->sym->isBaseOf(fc->sym,NULL)) { - Logger::println("static down cast"); - LLType* tolltype = DtoType(_to); - LLValue* rval = DtoBitCast(val->getRVal(), tolltype); - return new DImValue(_to, rval); - } - // class -> class - dynamic up cast - else { - Logger::println("dynamic up cast"); - return DtoDynamicCastObject(loc, val, _to); - } + Logger::println("from object"); + return DtoDynamicCastObject(loc, val, _to); } + } + // x -> class + else { + Logger::println("to class"); + // interface -> class + if (fc->sym->isInterfaceDeclaration()) { + Logger::println("interface cast"); + return DtoDynamicCastInterface(loc, val, _to); + } + // class -> class - static down cast + else if (tc->sym->isBaseOf(fc->sym, NULL)) { + Logger::println("static down cast"); + LLType *tolltype = DtoType(_to); + LLValue *rval = DtoBitCast(val->getRVal(), tolltype); + return new DImValue(_to, rval); + } + // class -> class - dynamic up cast + else { + Logger::println("dynamic up cast"); + return DtoDynamicCastObject(loc, val, _to); + } + } } ////////////////////////////////////////////////////////////////////////////////////////// -DValue* DtoDynamicCastObject(Loc& loc, DValue* val, Type* _to) -{ - // call: - // Object _d_dynamic_cast(Object o, ClassInfo c) +DValue *DtoDynamicCastObject(Loc &loc, DValue *val, Type *_to) { + // call: + // Object _d_dynamic_cast(Object o, ClassInfo c) - DtoResolveClass(ClassDeclaration::object); - DtoResolveClass(Type::typeinfoclass); + DtoResolveClass(ClassDeclaration::object); + DtoResolveClass(Type::typeinfoclass); - llvm::Function* func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_dynamic_cast"); - LLFunctionType* funcTy = func->getFunctionType(); + llvm::Function *func = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_dynamic_cast"); + LLFunctionType *funcTy = func->getFunctionType(); - // Object o - LLValue* obj = val->getRVal(); - obj = DtoBitCast(obj, funcTy->getParamType(0)); - assert(funcTy->getParamType(0) == obj->getType()); + // Object o + LLValue *obj = val->getRVal(); + obj = DtoBitCast(obj, funcTy->getParamType(0)); + assert(funcTy->getParamType(0) == obj->getType()); - // ClassInfo c - TypeClass* to = static_cast(_to->toBasetype()); - DtoResolveClass(to->sym); + // ClassInfo c + TypeClass *to = static_cast(_to->toBasetype()); + DtoResolveClass(to->sym); - LLValue* cinfo = getIrAggr(to->sym)->getClassInfoSymbol(); - // unfortunately this is needed as the implementation of object differs somehow from the declaration - // this could happen in user code as well :/ - cinfo = DtoBitCast(cinfo, funcTy->getParamType(1)); - assert(funcTy->getParamType(1) == cinfo->getType()); + LLValue *cinfo = getIrAggr(to->sym)->getClassInfoSymbol(); + // unfortunately this is needed as the implementation of object differs + // somehow from the declaration + // this could happen in user code as well :/ + cinfo = DtoBitCast(cinfo, funcTy->getParamType(1)); + assert(funcTy->getParamType(1) == cinfo->getType()); - // call it - LLValue* ret = gIR->CreateCallOrInvoke(func, obj, cinfo).getInstruction(); + // call it + LLValue *ret = gIR->CreateCallOrInvoke(func, obj, cinfo).getInstruction(); - // cast return value - ret = DtoBitCast(ret, DtoType(_to)); + // cast return value + ret = DtoBitCast(ret, DtoType(_to)); - return new DImValue(_to, ret); + return new DImValue(_to, ret); } ////////////////////////////////////////////////////////////////////////////////////////// -DValue* DtoDynamicCastInterface(Loc& loc, DValue* val, Type* _to) -{ - // call: - // Object _d_interface_cast(void* p, ClassInfo c) +DValue *DtoDynamicCastInterface(Loc &loc, DValue *val, Type *_to) { + // call: + // Object _d_interface_cast(void* p, ClassInfo c) - DtoResolveClass(ClassDeclaration::object); - DtoResolveClass(Type::typeinfoclass); + DtoResolveClass(ClassDeclaration::object); + DtoResolveClass(Type::typeinfoclass); - llvm::Function* func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_interface_cast"); - LLFunctionType* funcTy = func->getFunctionType(); + llvm::Function *func = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_interface_cast"); + LLFunctionType *funcTy = func->getFunctionType(); - // void* p - LLValue* ptr = val->getRVal(); - ptr = DtoBitCast(ptr, funcTy->getParamType(0)); + // void* p + LLValue *ptr = val->getRVal(); + ptr = DtoBitCast(ptr, funcTy->getParamType(0)); - // ClassInfo c - TypeClass* to = static_cast(_to->toBasetype()); - DtoResolveClass(to->sym); - LLValue* cinfo = getIrAggr(to->sym)->getClassInfoSymbol(); - // unfortunately this is needed as the implementation of object differs somehow from the declaration - // this could happen in user code as well :/ - cinfo = DtoBitCast(cinfo, funcTy->getParamType(1)); + // ClassInfo c + TypeClass *to = static_cast(_to->toBasetype()); + DtoResolveClass(to->sym); + LLValue *cinfo = getIrAggr(to->sym)->getClassInfoSymbol(); + // unfortunately this is needed as the implementation of object differs + // somehow from the declaration + // this could happen in user code as well :/ + cinfo = DtoBitCast(cinfo, funcTy->getParamType(1)); - // call it - LLValue* ret = gIR->CreateCallOrInvoke(func, ptr, cinfo).getInstruction(); + // call it + LLValue *ret = gIR->CreateCallOrInvoke(func, ptr, cinfo).getInstruction(); - // cast return value - ret = DtoBitCast(ret, DtoType(_to)); + // cast return value + ret = DtoBitCast(ret, DtoType(_to)); - return new DImValue(_to, ret); + return new DImValue(_to, ret); } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoVirtualFunctionPointer(DValue* inst, FuncDeclaration* fdecl, char* name) -{ - // sanity checks - assert(fdecl->isVirtual()); - assert(!fdecl->isFinalFunc()); - assert(inst->getType()->toBasetype()->ty == Tclass); - // 0 is always ClassInfo/Interface* unless it is a CPP interface - assert(fdecl->vtblIndex > 0 || (fdecl->vtblIndex == 0 && fdecl->linkage == LINKcpp)); +LLValue *DtoVirtualFunctionPointer(DValue *inst, FuncDeclaration *fdecl, + char *name) { + // sanity checks + assert(fdecl->isVirtual()); + assert(!fdecl->isFinalFunc()); + assert(inst->getType()->toBasetype()->ty == Tclass); + // 0 is always ClassInfo/Interface* unless it is a CPP interface + assert(fdecl->vtblIndex > 0 || + (fdecl->vtblIndex == 0 && fdecl->linkage == LINKcpp)); - // get instance - LLValue* vthis = inst->getRVal(); - IF_LOG Logger::cout() << "vthis: " << *vthis << '\n'; + // get instance + LLValue *vthis = inst->getRVal(); + IF_LOG Logger::cout() << "vthis: " << *vthis << '\n'; - LLValue* funcval = vthis; - // get the vtbl for objects - funcval = DtoGEPi(funcval, 0, 0); - // load vtbl ptr - funcval = DtoLoad(funcval); - // index vtbl - std::string vtblname = name; - vtblname.append("@vtbl"); - funcval = DtoGEPi(funcval, 0, fdecl->vtblIndex, vtblname.c_str()); - // load funcptr - funcval = DtoAlignedLoad(funcval); + LLValue *funcval = vthis; + // get the vtbl for objects + funcval = DtoGEPi(funcval, 0, 0); + // load vtbl ptr + funcval = DtoLoad(funcval); + // index vtbl + std::string vtblname = name; + vtblname.append("@vtbl"); + funcval = DtoGEPi(funcval, 0, fdecl->vtblIndex, vtblname.c_str()); + // load funcptr + funcval = DtoAlignedLoad(funcval); - IF_LOG Logger::cout() << "funcval: " << *funcval << '\n'; + IF_LOG Logger::cout() << "funcval: " << *funcval << '\n'; - // cast to final funcptr type - funcval = DtoBitCast(funcval, getPtrToType(DtoFunctionType(fdecl))); + // cast to final funcptr type + funcval = DtoBitCast(funcval, getPtrToType(DtoFunctionType(fdecl))); - // postpone naming until after casting to get the name in call instructions - funcval->setName(name); + // postpone naming until after casting to get the name in call instructions + funcval->setName(name); - IF_LOG Logger::cout() << "funcval casted: " << *funcval << '\n'; + IF_LOG Logger::cout() << "funcval casted: " << *funcval << '\n'; - return funcval; + return funcval; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -437,268 +437,257 @@ LLValue* DtoVirtualFunctionPointer(DValue* inst, FuncDeclaration* fdecl, char* n #if GENERATE_OFFTI // build a single element for the OffsetInfo[] of ClassInfo -static LLConstant* build_offti_entry(ClassDeclaration* cd, VarDeclaration* vd) -{ - std::vector inits(2); +static LLConstant *build_offti_entry(ClassDeclaration *cd, VarDeclaration *vd) { + std::vector inits(2); - // size_t offset; - // - assert(vd->ir.irField); - // grab the offset from llvm and the formal class type - size_t offset = gDataLayout->getStructLayout(isaStruct(cd->type->ir.type->get()))->getElementOffset(vd->ir.irField->index); - // offset nested struct/union fields - offset += vd->ir.irField->unionOffset; + // size_t offset; + // + assert(vd->ir.irField); + // grab the offset from llvm and the formal class type + size_t offset = + gDataLayout->getStructLayout(isaStruct(cd->type->ir.type->get())) + ->getElementOffset(vd->ir.irField->index); + // offset nested struct/union fields + offset += vd->ir.irField->unionOffset; - // assert that it matches DMD - Logger::println("offsets: %lu vs %u", offset, vd->offset); - assert(offset == vd->offset); + // assert that it matches DMD + Logger::println("offsets: %lu vs %u", offset, vd->offset); + assert(offset == vd->offset); - inits[0] = DtoConstSize_t(offset); + inits[0] = DtoConstSize_t(offset); - // TypeInfo ti; - inits[1] = DtoTypeInfoOf(vd->type, true); + // TypeInfo ti; + inits[1] = DtoTypeInfoOf(vd->type, true); - // done - return llvm::ConstantStruct::get(inits); + // done + return llvm::ConstantStruct::get(inits); } -static LLConstant* build_offti_array(ClassDeclaration* cd, LLType* arrayT) -{ - IrAggr* iraggr = cd->ir.irAggr; +static LLConstant *build_offti_array(ClassDeclaration *cd, LLType *arrayT) { + IrAggr *iraggr = cd->ir.irAggr; - size_t nvars = iraggr->varDecls.size(); - std::vector arrayInits(nvars); + size_t nvars = iraggr->varDecls.size(); + std::vector arrayInits(nvars); - for (size_t i=0; ivarDecls[i]); - } + for (size_t i = 0; i < nvars; i++) { + arrayInits[i] = build_offti_entry(cd, iraggr->varDecls[i]); + } - LLConstant* size = DtoConstSize_t(nvars); - LLConstant* ptr; + LLConstant *size = DtoConstSize_t(nvars); + LLConstant *ptr; - if (nvars == 0) - return LLConstant::getNullValue( arrayT ); + if (nvars == 0) + return LLConstant::getNullValue(arrayT); - // array type - LLArrayType* arrTy = llvm::ArrayType::get(arrayInits[0]->getType(), nvars); - LLConstant* arrInit = LLConstantArray::get(arrTy, arrayInits); + // array type + LLArrayType *arrTy = llvm::ArrayType::get(arrayInits[0]->getType(), nvars); + LLConstant *arrInit = LLConstantArray::get(arrTy, arrayInits); - // create symbol - llvm::GlobalVariable* gvar = getOrCreateGlobal(cd->loc, gIR->module, arrTy, - true, llvm::GlobalValue::InternalLinkage, arrInit, ".offti"); - ptr = DtoBitCast(gvar, getPtrToType(arrTy->getElementType())); + // create symbol + llvm::GlobalVariable *gvar = + getOrCreateGlobal(cd->loc, gIR->module, arrTy, true, + llvm::GlobalValue::InternalLinkage, arrInit, ".offti"); + ptr = DtoBitCast(gvar, getPtrToType(arrTy->getElementType())); - return DtoConstSlice(size, ptr); + return DtoConstSlice(size, ptr); } #endif // GENERATE_OFFTI -static LLConstant* build_class_dtor(ClassDeclaration* cd) -{ - FuncDeclaration* dtor = cd->dtor; +static LLConstant *build_class_dtor(ClassDeclaration *cd) { + FuncDeclaration *dtor = cd->dtor; - // if no destructor emit a null - if (!dtor) - return getNullPtr(getVoidPtrType()); + // if no destructor emit a null + if (!dtor) + return getNullPtr(getVoidPtrType()); - DtoResolveFunction(dtor); - return llvm::ConstantExpr::getBitCast(getIrFunc(dtor)->func, getPtrToType(LLType::getInt8Ty(gIR->context()))); + DtoResolveFunction(dtor); + return llvm::ConstantExpr::getBitCast( + getIrFunc(dtor)->func, getPtrToType(LLType::getInt8Ty(gIR->context()))); } -static ClassFlags::Type build_classinfo_flags(ClassDeclaration* cd) -{ - // adapted from original dmd code: - // toobj.c: ToObjFile::visit(ClassDeclaration*) and ToObjFile::visit(InterfaceDeclaration*) - - ClassFlags::Type flags = ClassFlags::hasOffTi | ClassFlags::hasTypeInfo; - if (cd->isInterfaceDeclaration()) - { - if (cd->isCOMinterface()) flags |= ClassFlags::isCOMclass; - return flags; - } - - if (cd->isCOMclass()) flags |= ClassFlags::isCOMclass; - if (cd->isCPPclass()) flags |= ClassFlags::isCPPclass; - flags |= ClassFlags::hasGetMembers; - if (cd->ctor) - flags |= ClassFlags::hasCtor; - for (ClassDeclaration *pc = cd; pc; pc = pc->baseClass) - { - if (pc->dtor) - { - flags |= ClassFlags::hasDtor; - break; - } - } - if (cd->isabstract) - flags |= ClassFlags::isAbstract; - for (ClassDeclaration *pc = cd; pc; pc = pc->baseClass) - { - if (pc->members) - { - for (size_t i = 0; i < pc->members->dim; i++) - { - Dsymbol *sm = (*pc->members)[i]; - //printf("sm = %s %s\n", sm->kind(), sm->toChars()); - if (sm->hasPointers()) - return flags; - } - } - } - flags |= ClassFlags::noPointers; +static ClassFlags::Type build_classinfo_flags(ClassDeclaration *cd) { + // adapted from original dmd code: + // toobj.c: ToObjFile::visit(ClassDeclaration*) and + // ToObjFile::visit(InterfaceDeclaration*) + ClassFlags::Type flags = ClassFlags::hasOffTi | ClassFlags::hasTypeInfo; + if (cd->isInterfaceDeclaration()) { + if (cd->isCOMinterface()) + flags |= ClassFlags::isCOMclass; return flags; + } + + if (cd->isCOMclass()) + flags |= ClassFlags::isCOMclass; + if (cd->isCPPclass()) + flags |= ClassFlags::isCPPclass; + flags |= ClassFlags::hasGetMembers; + if (cd->ctor) + flags |= ClassFlags::hasCtor; + for (ClassDeclaration *pc = cd; pc; pc = pc->baseClass) { + if (pc->dtor) { + flags |= ClassFlags::hasDtor; + break; + } + } + if (cd->isabstract) + flags |= ClassFlags::isAbstract; + for (ClassDeclaration *pc = cd; pc; pc = pc->baseClass) { + if (pc->members) { + for (size_t i = 0; i < pc->members->dim; i++) { + Dsymbol *sm = (*pc->members)[i]; + // printf("sm = %s %s\n", sm->kind(), sm->toChars()); + if (sm->hasPointers()) + return flags; + } + } + } + flags |= ClassFlags::noPointers; + + return flags; } -LLConstant* DtoDefineClassInfo(ClassDeclaration* cd) -{ -// The layout is: -// { -// void **vptr; -// monitor_t monitor; -// byte[] init; -// string name; -// void*[] vtbl; -// Interface[] interfaces; -// TypeInfo_Class base; -// void *destructor; -// void function(Object) classInvariant; -// ClassFlags m_flags; -// void* deallocator; -// OffsetTypeInfo[] m_offTi; -// void function(Object) defaultConstructor; -// immutable(void)* m_RTInfo; -// } +LLConstant *DtoDefineClassInfo(ClassDeclaration *cd) { + // The layout is: + // { + // void **vptr; + // monitor_t monitor; + // byte[] init; + // string name; + // void*[] vtbl; + // Interface[] interfaces; + // TypeInfo_Class base; + // void *destructor; + // void function(Object) classInvariant; + // ClassFlags m_flags; + // void* deallocator; + // OffsetTypeInfo[] m_offTi; + // void function(Object) defaultConstructor; + // immutable(void)* m_RTInfo; + // } - IF_LOG Logger::println("DtoDefineClassInfo(%s)", cd->toChars()); - LOG_SCOPE; + IF_LOG Logger::println("DtoDefineClassInfo(%s)", cd->toChars()); + LOG_SCOPE; - assert(cd->type->ty == Tclass); + assert(cd->type->ty == Tclass); - IrAggr* ir = getIrAggr(cd); - ClassDeclaration* cinfo = Type::typeinfoclass; + IrAggr *ir = getIrAggr(cd); + ClassDeclaration *cinfo = Type::typeinfoclass; - if (cinfo->fields.dim != 12) - { - error(Loc(), "Unexpected number of fields in object.ClassInfo; " - "druntime version does not match compiler (see -v)"); - fatal(); - } + if (cinfo->fields.dim != 12) { + error(Loc(), "Unexpected number of fields in object.ClassInfo; " + "druntime version does not match compiler (see -v)"); + fatal(); + } - // use the rtti builder - RTTIBuilder b(cinfo); + // use the rtti builder + RTTIBuilder b(cinfo); - LLConstant* c; + LLConstant *c; - LLType* voidPtr = getVoidPtrType(); - LLType* voidPtrPtr = getPtrToType(voidPtr); + LLType *voidPtr = getVoidPtrType(); + LLType *voidPtrPtr = getPtrToType(voidPtr); - // adapted from original dmd code - // init[] - if (cd->isInterfaceDeclaration()) - { - b.push_null_void_array(); - } - else - { - size_t initsz = cd->size(Loc()); - b.push_void_array(initsz, ir->getInitSymbol()); - } + // adapted from original dmd code + // init[] + if (cd->isInterfaceDeclaration()) { + b.push_null_void_array(); + } else { + size_t initsz = cd->size(Loc()); + b.push_void_array(initsz, ir->getInitSymbol()); + } - // name[] - const char *name = cd->ident->toChars(); - size_t namelen = strlen(name); - if (!(namelen > 9 && memcmp(name, "TypeInfo_", 9) == 0)) - { - name = cd->toPrettyChars(); - namelen = strlen(name); - } - b.push_string(name); + // name[] + const char *name = cd->ident->toChars(); + size_t namelen = strlen(name); + if (!(namelen > 9 && memcmp(name, "TypeInfo_", 9) == 0)) { + name = cd->toPrettyChars(); + namelen = strlen(name); + } + b.push_string(name); - // vtbl[] - if (cd->isInterfaceDeclaration()) - { - b.push_array(0, getNullValue(voidPtrPtr)); - } - else - { - c = DtoBitCast(ir->getVtblSymbol(), voidPtrPtr); - b.push_array(cd->vtbl.dim, c); - } + // vtbl[] + if (cd->isInterfaceDeclaration()) { + b.push_array(0, getNullValue(voidPtrPtr)); + } else { + c = DtoBitCast(ir->getVtblSymbol(), voidPtrPtr); + b.push_array(cd->vtbl.dim, c); + } - // interfaces[] - b.push(ir->getClassInfoInterfaces()); + // interfaces[] + b.push(ir->getClassInfoInterfaces()); - // base - // interfaces never get a base, just the interfaces[] - if (cd->baseClass && !cd->isInterfaceDeclaration()) - b.push_classinfo(cd->baseClass); - else - b.push_null(cinfo->type); + // base + // interfaces never get a base, just the interfaces[] + if (cd->baseClass && !cd->isInterfaceDeclaration()) + b.push_classinfo(cd->baseClass); + else + b.push_null(cinfo->type); - // destructor - if (cd->isInterfaceDeclaration()) - b.push_null_vp(); - else - b.push(build_class_dtor(cd)); + // destructor + if (cd->isInterfaceDeclaration()) + b.push_null_vp(); + else + b.push(build_class_dtor(cd)); - // invariant - VarDeclaration* invVar = cinfo->fields[6]; - b.push_funcptr(cd->inv, invVar->type); + // invariant + VarDeclaration *invVar = cinfo->fields[6]; + b.push_funcptr(cd->inv, invVar->type); - // flags - ClassFlags::Type flags = build_classinfo_flags(cd); - b.push_uint(flags); + // flags + ClassFlags::Type flags = build_classinfo_flags(cd); + b.push_uint(flags); - // deallocator - b.push_funcptr(cd->aggDelete, Type::tvoid->pointerTo()); + // deallocator + b.push_funcptr(cd->aggDelete, Type::tvoid->pointerTo()); - // offset typeinfo - VarDeclaration* offTiVar = cinfo->fields[9]; + // offset typeinfo + VarDeclaration *offTiVar = cinfo->fields[9]; #if GENERATE_OFFTI - if (cd->isInterfaceDeclaration()) - b.push_null(offTiVar->type); - else - b.push(build_offti_array(cd, DtoType(offTiVar->type))); -#else + if (cd->isInterfaceDeclaration()) b.push_null(offTiVar->type); + else + b.push(build_offti_array(cd, DtoType(offTiVar->type))); +#else + b.push_null(offTiVar->type); #endif - // defaultConstructor - VarDeclaration* defConstructorVar = cinfo->fields.data[10]; - CtorDeclaration* defConstructor = cd->defaultCtor; - if (defConstructor && (defConstructor->storage_class & STCdisable)) - defConstructor = NULL; - b.push_funcptr(defConstructor, defConstructorVar->type); + // defaultConstructor + VarDeclaration *defConstructorVar = cinfo->fields.data[10]; + CtorDeclaration *defConstructor = cd->defaultCtor; + if (defConstructor && (defConstructor->storage_class & STCdisable)) + defConstructor = NULL; + b.push_funcptr(defConstructor, defConstructorVar->type); - // m_RTInfo - // The cases where getRTInfo is null are not quite here, but the code is - // modelled after what DMD does. - if (cd->getRTInfo) - b.push(toConstElem(cd->getRTInfo, gIR)); - else if (flags & ClassFlags::noPointers) - b.push_size_as_vp(0); // no pointers - else - b.push_size_as_vp(1); // has pointers + // m_RTInfo + // The cases where getRTInfo is null are not quite here, but the code is + // modelled after what DMD does. + if (cd->getRTInfo) + b.push(toConstElem(cd->getRTInfo, gIR)); + else if (flags & ClassFlags::noPointers) + b.push_size_as_vp(0); // no pointers + else + b.push_size_as_vp(1); // has pointers - /*size_t n = inits.size(); - for (size_t i=0; iclassInfo->getType()->getContainedType(0); - LLConstant* finalinit = b.get_constant(isaStruct(initType)); + // build the initializer + LLType *initType = ir->classInfo->getType()->getContainedType(0); + LLConstant *finalinit = b.get_constant(isaStruct(initType)); - //Logger::cout() << "built the classinfo initializer:\n" << *finalinit <<'\n'; - ir->constClassInfo = finalinit; + // Logger::cout() << "built the classinfo initializer:\n" << *finalinit + // <<'\n'; + ir->constClassInfo = finalinit; - // sanity check - assert(finalinit->getType() == initType && - "__ClassZ initializer does not match the ClassInfo type"); + // sanity check + assert(finalinit->getType() == initType && + "__ClassZ initializer does not match the ClassInfo type"); - // return initializer - return finalinit; + // return initializer + return finalinit; } diff --git a/gen/classes.h b/gen/classes.h index c625807fd7..c58272152e 100644 --- a/gen/classes.h +++ b/gen/classes.h @@ -24,21 +24,22 @@ class NewExp; class TypeClass; /// Resolves the llvm type for a class declaration -void DtoResolveClass(ClassDeclaration* cd); +void DtoResolveClass(ClassDeclaration *cd); /// Builds the initializer of cd's ClassInfo. /// FIXME: this should be put into IrStruct and eventually IrClass. -llvm::Constant* DtoDefineClassInfo(ClassDeclaration* cd); +llvm::Constant *DtoDefineClassInfo(ClassDeclaration *cd); -DValue* DtoNewClass(Loc& loc, TypeClass* type, NewExp* newexp); -void DtoInitClass(TypeClass* tc, llvm::Value* dst); -void DtoFinalizeClass(Loc& loc, llvm::Value* inst); +DValue *DtoNewClass(Loc &loc, TypeClass *type, NewExp *newexp); +void DtoInitClass(TypeClass *tc, llvm::Value *dst); +void DtoFinalizeClass(Loc &loc, llvm::Value *inst); -DValue* DtoCastClass(Loc& loc, DValue* val, Type* to); -DValue* DtoDynamicCastObject(Loc& loc, DValue* val, Type* to); +DValue *DtoCastClass(Loc &loc, DValue *val, Type *to); +DValue *DtoDynamicCastObject(Loc &loc, DValue *val, Type *to); -DValue* DtoDynamicCastInterface(Loc& loc, DValue* val, Type* to); +DValue *DtoDynamicCastInterface(Loc &loc, DValue *val, Type *to); -llvm::Value* DtoVirtualFunctionPointer(DValue* inst, FuncDeclaration* fdecl, char* name); +llvm::Value *DtoVirtualFunctionPointer(DValue *inst, FuncDeclaration *fdecl, + char *name); #endif diff --git a/gen/complex.cpp b/gen/complex.cpp index bf5721eacd..2767f33624 100644 --- a/gen/complex.cpp +++ b/gen/complex.cpp @@ -19,457 +19,465 @@ ////////////////////////////////////////////////////////////////////////////////////////// -llvm::StructType* DtoComplexType(Type* type) -{ - Type* t = type->toBasetype(); - LLType* base = DtoComplexBaseType(t); - LLType* types[] = { base, base }; - return llvm::StructType::get(gIR->context(), types, false); +llvm::StructType *DtoComplexType(Type *type) { + Type *t = type->toBasetype(); + LLType *base = DtoComplexBaseType(t); + LLType *types[] = {base, base}; + return llvm::StructType::get(gIR->context(), types, false); } -LLType* DtoComplexBaseType(Type* t) -{ - switch (t->toBasetype()->ty) { - default: llvm_unreachable("Unexpected complex floating point type"); - case Tcomplex32: return DtoType(Type::basic[Tfloat32]); - case Tcomplex64: return DtoType(Type::basic[Tfloat64]); - case Tcomplex80: return DtoType(Type::basic[Tfloat80]); - } +LLType *DtoComplexBaseType(Type *t) { + switch (t->toBasetype()->ty) { + default: + llvm_unreachable("Unexpected complex floating point type"); + case Tcomplex32: + return DtoType(Type::basic[Tfloat32]); + case Tcomplex64: + return DtoType(Type::basic[Tfloat64]); + case Tcomplex80: + return DtoType(Type::basic[Tfloat80]); + } } ////////////////////////////////////////////////////////////////////////////////////////// -LLConstant* DtoConstComplex(Type* _ty, longdouble re, longdouble im) -{ - Type* base = 0; - switch (_ty->toBasetype()->ty) { - default: llvm_unreachable("Unexpected complex floating point type"); - case Tcomplex32: base = Type::tfloat32; break; - case Tcomplex64: base = Type::tfloat64; break; - case Tcomplex80: base = Type::tfloat80; break; - } +LLConstant *DtoConstComplex(Type *_ty, longdouble re, longdouble im) { + Type *base = 0; + switch (_ty->toBasetype()->ty) { + default: + llvm_unreachable("Unexpected complex floating point type"); + case Tcomplex32: + base = Type::tfloat32; + break; + case Tcomplex64: + base = Type::tfloat64; + break; + case Tcomplex80: + base = Type::tfloat80; + break; + } - LLConstant * inits[] = { DtoConstFP(base, re), DtoConstFP(base, im) }; + LLConstant *inits[] = {DtoConstFP(base, re), DtoConstFP(base, im)}; - return llvm::ConstantStruct::get(DtoComplexType(_ty), - llvm::ArrayRef(inits)); + return llvm::ConstantStruct::get(DtoComplexType(_ty), + llvm::ArrayRef(inits)); } ////////////////////////////////////////////////////////////////////////////////////////// -DValue* DtoComplex(Loc& loc, Type* to, DValue* val) -{ - LLType* complexTy = DtoType(to); +DValue *DtoComplex(Loc &loc, Type *to, DValue *val) { + LLType *complexTy = DtoType(to); - Type* baserety; - Type* baseimty; - switch (to->toBasetype()->ty) { - default: llvm_unreachable("Unexpected complex floating point type"); - case Tcomplex32: - baserety = Type::tfloat32; - baseimty = Type::timaginary32; - break; - case Tcomplex64: - baserety = Type::tfloat64; - baseimty = Type::timaginary64; - break; - case Tcomplex80: - baserety = Type::tfloat80; - baseimty = Type::timaginary80; - break; - } + Type *baserety; + Type *baseimty; + switch (to->toBasetype()->ty) { + default: + llvm_unreachable("Unexpected complex floating point type"); + case Tcomplex32: + baserety = Type::tfloat32; + baseimty = Type::timaginary32; + break; + case Tcomplex64: + baserety = Type::tfloat64; + baseimty = Type::timaginary64; + break; + case Tcomplex80: + baserety = Type::tfloat80; + baseimty = Type::timaginary80; + break; + } - LLValue *re, *im; - DtoGetComplexParts(loc, to, val, re, im); + LLValue *re, *im; + DtoGetComplexParts(loc, to, val, re, im); - if(!re) - re = LLConstant::getNullValue(DtoType(baserety)); - if(!im) - im = LLConstant::getNullValue(DtoType(baseimty)); + if (!re) + re = LLConstant::getNullValue(DtoType(baserety)); + if (!im) + im = LLConstant::getNullValue(DtoType(baseimty)); - LLValue* res = DtoAggrPair(complexTy, re, im); + LLValue *res = DtoAggrPair(complexTy, re, im); - return new DImValue(to, res); + return new DImValue(to, res); } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoComplexSet(LLValue* c, LLValue* re, LLValue* im) -{ - DtoStore(re, DtoGEPi(c, 0, 0)); - DtoStore(im, DtoGEPi(c, 0, 1)); +void DtoComplexSet(LLValue *c, LLValue *re, LLValue *im) { + DtoStore(re, DtoGEPi(c, 0, 0)); + DtoStore(im, DtoGEPi(c, 0, 1)); } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoGetComplexParts(Loc& loc, Type* to, DValue* val, DValue*& re, DValue*& im) -{ - Type* baserety; - Type* baseimty; - switch (to->toBasetype()->ty) { - default: llvm_unreachable("Unexpected complex floating point type"); - case Tcomplex32: - baserety = Type::tfloat32; - baseimty = Type::timaginary32; - break; - case Tcomplex64: - baserety = Type::tfloat64; - baseimty = Type::timaginary64; - break; - case Tcomplex80: - baserety = Type::tfloat80; - baseimty = Type::timaginary80; - break; - } +void DtoGetComplexParts(Loc &loc, Type *to, DValue *val, DValue *&re, + DValue *&im) { + Type *baserety; + Type *baseimty; + switch (to->toBasetype()->ty) { + default: + llvm_unreachable("Unexpected complex floating point type"); + case Tcomplex32: + baserety = Type::tfloat32; + baseimty = Type::timaginary32; + break; + case Tcomplex64: + baserety = Type::tfloat64; + baseimty = Type::timaginary64; + break; + case Tcomplex80: + baserety = Type::tfloat80; + baseimty = Type::timaginary80; + break; + } - Type* t = val->getType()->toBasetype(); + Type *t = val->getType()->toBasetype(); - if (t->iscomplex()) { - DValue* v = DtoCastComplex(loc, val, to); - if (to->iscomplex()) { - if (v->isLVal()) { - LLValue *reVal = DtoGEP(v->getLVal(), DtoConstInt(0), DtoConstInt(0), ".re_part"); - LLValue *imVal = DtoGEP(v->getLVal(), DtoConstInt(0), DtoConstInt(1), ".im_part"); - re = new DVarValue(baserety, reVal); - im = new DVarValue(baseimty, imVal); - } else { - LLValue *reVal = gIR->ir->CreateExtractValue(v->getRVal(), 0, ".re_part"); - LLValue *imVal = gIR->ir->CreateExtractValue(v->getRVal(), 1, ".im_part"); - re = new DImValue(baserety, reVal); - im = new DImValue(baseimty, imVal); - } - } else - DtoGetComplexParts(loc, to, v, re, im); - } - else if (t->isimaginary()) { - re = NULL; - im = DtoCastFloat(loc, val, baseimty); - } - else if (t->isfloating()) { - re = DtoCastFloat(loc, val, baserety); - im = NULL; - } - else if (t->isintegral()) { - re = DtoCastInt(loc, val, baserety); - im = NULL; - } - else { - llvm_unreachable("Unexpected numeric type."); - } -} - -////////////////////////////////////////////////////////////////////////////////////////// - -void DtoGetComplexParts(Loc& loc, Type* to, DValue* val, LLValue*& re, LLValue*& im) -{ - DValue *dre, *dim; - DtoGetComplexParts(loc, to, val, dre, dim); - re = dre ? dre->getRVal() : 0; - im = dim ? dim->getRVal() : 0; -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DValue* DtoComplexAdd(Loc& loc, Type* type, DValue* lhs, DValue* rhs) -{ - llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im; - - // lhs values - DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); - // rhs values - DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); - - // add up - if(lhs_re && rhs_re) - res_re = gIR->ir->CreateFAdd(lhs_re, rhs_re); - else if(lhs_re) - res_re = lhs_re; - else // either rhs_re or no re at all (then use any) - res_re = rhs_re; - - if(lhs_im && rhs_im) - res_im = gIR->ir->CreateFAdd(lhs_im, rhs_im); - else if(lhs_im) - res_im = lhs_im; - else // either rhs_im or no im at all (then use any) - res_im = rhs_im; - - LLValue* res = DtoAggrPair(DtoType(type), res_re, res_im); - return new DImValue(type, res); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DValue* DtoComplexSub(Loc& loc, Type* type, DValue* lhs, DValue* rhs) -{ - llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im; - - // lhs values - DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); - // rhs values - DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); - - // add up - if(lhs_re && rhs_re) - res_re = gIR->ir->CreateFSub(lhs_re, rhs_re); - else if(lhs_re) - res_re = lhs_re; - else // either rhs_re or no re at all (then use any) - res_re = gIR->ir->CreateFNeg(rhs_re, "neg"); - - if(lhs_im && rhs_im) - res_im = gIR->ir->CreateFSub(lhs_im, rhs_im); - else if(lhs_im) - res_im = lhs_im; - else // either rhs_im or no im at all (then use any) - res_im = gIR->ir->CreateFNeg(rhs_im, "neg"); - - LLValue* res = DtoAggrPair(DtoType(type), res_re, res_im); - return new DImValue(type, res); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DValue* DtoComplexMul(Loc& loc, Type* type, DValue* lhs, DValue* rhs) -{ - llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im; - - // lhs values - DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); - // rhs values - DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); - - // mul up - llvm::Value *rere = NULL; - llvm::Value *reim = NULL; - llvm::Value *imre = NULL; - llvm::Value *imim = NULL; - - if(lhs_re && rhs_re) - rere = gIR->ir->CreateFMul(lhs_re, rhs_re, "rere_mul"); - if(lhs_re && rhs_im) - reim = gIR->ir->CreateFMul(lhs_re, rhs_im, "reim_mul"); - if(lhs_im && rhs_re) - imre = gIR->ir->CreateFMul(lhs_im, rhs_re, "imre_mul"); - if(lhs_im && rhs_im) - imim = gIR->ir->CreateFMul(lhs_im, rhs_im, "imim_mul"); - - if(rere && imim) - res_re = gIR->ir->CreateFSub(rere, imim, "rere_imim_sub"); - else if(rere) - res_re = rere; - else if(imim) - res_re = gIR->ir->CreateFNeg(imim, "imim_neg"); - else - res_re = lhs_re ? rhs_re : lhs_re; // null! - - if(reim && imre) - res_im = gIR->ir->CreateFAdd(reim, imre, "reim_imre_add"); - else if(reim) - res_im = reim; - else if(imre) - res_im = imre; - else - res_im = lhs_re ? rhs_im : lhs_re; // null! - - LLValue* res = DtoAggrPair(DtoType(type), res_re, res_im); - return new DImValue(type, res); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DValue* DtoComplexDiv(Loc& loc, Type* type, DValue* lhs, DValue* rhs) -{ - llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im; - - // lhs values - DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); - // rhs values - DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); - - // if divisor is only real, division is simple - if(rhs_re && !rhs_im) { - if(lhs_re) - res_re = gIR->ir->CreateFDiv(lhs_re, rhs_re, "re_divby_re"); - else - res_re = lhs_re; - if(lhs_im) - res_im = gIR->ir->CreateFDiv(lhs_im, rhs_re, "im_divby_re"); - else - res_im = lhs_im; - } - // if divisor is only imaginary, division is simple too - else if(!rhs_re && rhs_im) { - if(lhs_re) - res_im = gIR->ir->CreateFNeg(gIR->ir->CreateFDiv(lhs_re, rhs_im, "re_divby_im"), "neg"); - else - res_im = lhs_re; - if(lhs_im) - res_re = gIR->ir->CreateFDiv(lhs_im, rhs_im, "im_divby_im"); - else - res_re = lhs_im; - } - // full division - else { - llvm::Value *tmp1, *tmp2, *denom; - - if(lhs_re && lhs_im) { - tmp1 = gIR->ir->CreateFMul(lhs_re, rhs_re, "rere"); - tmp2 = gIR->ir->CreateFMul(lhs_im, rhs_im, "imim"); - res_re = gIR->ir->CreateFAdd(tmp1, tmp2, "rere_plus_imim"); - - tmp1 = gIR->ir->CreateFMul(lhs_re, rhs_im, "reim"); - tmp2 = gIR->ir->CreateFMul(lhs_im, rhs_re, "imre"); - res_im = gIR->ir->CreateFSub(tmp2, tmp1, "imre_sub_reim"); - } - else if(lhs_re) { - res_re = gIR->ir->CreateFMul(lhs_re, rhs_re, "rere"); - - res_im = gIR->ir->CreateFMul(lhs_re, rhs_im, "reim"); - res_im = gIR->ir->CreateFNeg(res_im); - } - else if(lhs_im) { - res_re = gIR->ir->CreateFMul(lhs_im, rhs_im, "imim"); - res_im = gIR->ir->CreateFMul(lhs_im, rhs_re, "imre"); - } - else - llvm_unreachable("lhs has neither real nor imaginary part"); - - tmp1 = gIR->ir->CreateFMul(rhs_re, rhs_re, "rhs_resq"); - tmp2 = gIR->ir->CreateFMul(rhs_im, rhs_im, "rhs_imsq"); - denom = gIR->ir->CreateFAdd(tmp1, tmp2, "denom"); - - res_re = gIR->ir->CreateFDiv(res_re, denom, "res_re"); - res_im = gIR->ir->CreateFDiv(res_im, denom, "res_im"); - } - - LLValue* res = DtoAggrPair(DtoType(type), res_re, res_im); - return new DImValue(type, res); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DValue* DtoComplexRem(Loc& loc, Type* type, DValue* lhs, DValue* rhs) -{ - llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im, *divisor; - - // lhs values - DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); - // rhs values - DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); - - // Divisor can be real or imaginary but not complex - assert((rhs_re != 0) ^ (rhs_im != 0)); - - divisor = rhs_re ? rhs_re : rhs_im; - res_re = lhs_re ? gIR->ir->CreateFRem(lhs_re, divisor) : lhs_re; - res_im = lhs_re ? gIR->ir->CreateFRem(lhs_im, divisor) : lhs_im; - - LLValue* res = DtoAggrPair(DtoType(type), res_re, res_im); - return new DImValue(type, res); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DValue* DtoComplexNeg(Loc& loc, Type* type, DValue* val) -{ - llvm::Value *a, *b, *re, *im; - - // values - DtoGetComplexParts(loc, type, val, a, b); - - // neg up - assert(a && b); - re = gIR->ir->CreateFNeg(a); - im = gIR->ir->CreateFNeg(b); - - LLValue* res = DtoAggrPair(DtoType(type), re, im); - return new DImValue(type, res); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LLValue* DtoComplexEquals(Loc& loc, TOK op, DValue* lhs, DValue* rhs) -{ - Type* type = lhs->getType(); - - DValue *lhs_re, *lhs_im, *rhs_re, *rhs_im; - - // lhs values - DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); - // rhs values - DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); - - // (l.re==r.re && l.im==r.im) or (l.re!=r.re || l.im!=r.im) - LLValue* b1 = DtoBinFloatsEquals(loc, lhs_re, rhs_re, op); - LLValue* b2 = DtoBinFloatsEquals(loc, lhs_im, rhs_im, op); - - if (op == TOKequal) - return gIR->ir->CreateAnd(b1, b2); - else - return gIR->ir->CreateOr(b1, b2); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DValue* DtoCastComplex(Loc& loc, DValue* val, Type* _to) -{ - Type* to = _to->toBasetype(); - Type* vty = val->getType()->toBasetype(); + if (t->iscomplex()) { + DValue *v = DtoCastComplex(loc, val, to); if (to->iscomplex()) { - if (vty->size() == to->size()) - return val; - - llvm::Value *re, *im; - DtoGetComplexParts(loc, val->getType(), val, re, im); - LLType* toty = DtoComplexBaseType(to); - - if (to->size() < vty->size()) { - re = gIR->ir->CreateFPTrunc(re, toty); - im = gIR->ir->CreateFPTrunc(im, toty); - } - else { - re = gIR->ir->CreateFPExt(re, toty); - im = gIR->ir->CreateFPExt(im, toty); - } - - LLValue* pair = DtoAggrPair(DtoType(_to), re, im); - return new DImValue(_to, pair); - } - else if (to->isimaginary()) { - // FIXME: this loads both values, even when we only need one - LLValue* v = val->getRVal(); - LLValue* impart = gIR->ir->CreateExtractValue(v, 1, ".im_part"); - Type *extractty; - switch (vty->ty) { - default: llvm_unreachable("Unexpected complex floating point type"); - case Tcomplex32: extractty = Type::timaginary32; break; - case Tcomplex64: extractty = Type::timaginary64; break; - case Tcomplex80: extractty = Type::timaginary80; break; - } - DImValue* im = new DImValue(extractty, impart); - return DtoCastFloat(loc, im, to); - } - else if (to->ty == Tbool) { - return new DImValue(_to, DtoComplexEquals(loc, TOKnotequal, val, DtoNullValue(vty))); - } - else if (to->isfloating() || to->isintegral()) { - // FIXME: this loads both values, even when we only need one - LLValue* v = val->getRVal(); - LLValue* repart = gIR->ir->CreateExtractValue(v, 0, ".re_part"); - Type *extractty; - switch (vty->ty) { - default: llvm_unreachable("Unexpected complex floating point type"); - case Tcomplex32: extractty = Type::tfloat32; break; - case Tcomplex64: extractty = Type::tfloat64; break; - case Tcomplex80: extractty = Type::tfloat80; break; - } - DImValue* re = new DImValue(extractty, repart); - return DtoCastFloat(loc, re, to); - } - else { - error(loc, "Don't know how to cast %s to %s", vty->toChars(), to->toChars()); - fatal(); - } + if (v->isLVal()) { + LLValue *reVal = + DtoGEP(v->getLVal(), DtoConstInt(0), DtoConstInt(0), ".re_part"); + LLValue *imVal = + DtoGEP(v->getLVal(), DtoConstInt(0), DtoConstInt(1), ".im_part"); + re = new DVarValue(baserety, reVal); + im = new DVarValue(baseimty, imVal); + } else { + LLValue *reVal = + gIR->ir->CreateExtractValue(v->getRVal(), 0, ".re_part"); + LLValue *imVal = + gIR->ir->CreateExtractValue(v->getRVal(), 1, ".im_part"); + re = new DImValue(baserety, reVal); + im = new DImValue(baseimty, imVal); + } + } else + DtoGetComplexParts(loc, to, v, re, im); + } else if (t->isimaginary()) { + re = NULL; + im = DtoCastFloat(loc, val, baseimty); + } else if (t->isfloating()) { + re = DtoCastFloat(loc, val, baserety); + im = NULL; + } else if (t->isintegral()) { + re = DtoCastInt(loc, val, baserety); + im = NULL; + } else { + llvm_unreachable("Unexpected numeric type."); + } } +////////////////////////////////////////////////////////////////////////////////////////// + +void DtoGetComplexParts(Loc &loc, Type *to, DValue *val, LLValue *&re, + LLValue *&im) { + DValue *dre, *dim; + DtoGetComplexParts(loc, to, val, dre, dim); + re = dre ? dre->getRVal() : 0; + im = dim ? dim->getRVal() : 0; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DValue *DtoComplexAdd(Loc &loc, Type *type, DValue *lhs, DValue *rhs) { + llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im; + + // lhs values + DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); + // rhs values + DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); + + // add up + if (lhs_re && rhs_re) + res_re = gIR->ir->CreateFAdd(lhs_re, rhs_re); + else if (lhs_re) + res_re = lhs_re; + else // either rhs_re or no re at all (then use any) + res_re = rhs_re; + + if (lhs_im && rhs_im) + res_im = gIR->ir->CreateFAdd(lhs_im, rhs_im); + else if (lhs_im) + res_im = lhs_im; + else // either rhs_im or no im at all (then use any) + res_im = rhs_im; + + LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im); + return new DImValue(type, res); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DValue *DtoComplexSub(Loc &loc, Type *type, DValue *lhs, DValue *rhs) { + llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im; + + // lhs values + DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); + // rhs values + DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); + + // add up + if (lhs_re && rhs_re) + res_re = gIR->ir->CreateFSub(lhs_re, rhs_re); + else if (lhs_re) + res_re = lhs_re; + else // either rhs_re or no re at all (then use any) + res_re = gIR->ir->CreateFNeg(rhs_re, "neg"); + + if (lhs_im && rhs_im) + res_im = gIR->ir->CreateFSub(lhs_im, rhs_im); + else if (lhs_im) + res_im = lhs_im; + else // either rhs_im or no im at all (then use any) + res_im = gIR->ir->CreateFNeg(rhs_im, "neg"); + + LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im); + return new DImValue(type, res); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DValue *DtoComplexMul(Loc &loc, Type *type, DValue *lhs, DValue *rhs) { + llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im; + + // lhs values + DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); + // rhs values + DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); + + // mul up + llvm::Value *rere = NULL; + llvm::Value *reim = NULL; + llvm::Value *imre = NULL; + llvm::Value *imim = NULL; + + if (lhs_re && rhs_re) + rere = gIR->ir->CreateFMul(lhs_re, rhs_re, "rere_mul"); + if (lhs_re && rhs_im) + reim = gIR->ir->CreateFMul(lhs_re, rhs_im, "reim_mul"); + if (lhs_im && rhs_re) + imre = gIR->ir->CreateFMul(lhs_im, rhs_re, "imre_mul"); + if (lhs_im && rhs_im) + imim = gIR->ir->CreateFMul(lhs_im, rhs_im, "imim_mul"); + + if (rere && imim) + res_re = gIR->ir->CreateFSub(rere, imim, "rere_imim_sub"); + else if (rere) + res_re = rere; + else if (imim) + res_re = gIR->ir->CreateFNeg(imim, "imim_neg"); + else + res_re = lhs_re ? rhs_re : lhs_re; // null! + + if (reim && imre) + res_im = gIR->ir->CreateFAdd(reim, imre, "reim_imre_add"); + else if (reim) + res_im = reim; + else if (imre) + res_im = imre; + else + res_im = lhs_re ? rhs_im : lhs_re; // null! + + LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im); + return new DImValue(type, res); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DValue *DtoComplexDiv(Loc &loc, Type *type, DValue *lhs, DValue *rhs) { + llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im; + + // lhs values + DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); + // rhs values + DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); + + // if divisor is only real, division is simple + if (rhs_re && !rhs_im) { + if (lhs_re) + res_re = gIR->ir->CreateFDiv(lhs_re, rhs_re, "re_divby_re"); + else + res_re = lhs_re; + if (lhs_im) + res_im = gIR->ir->CreateFDiv(lhs_im, rhs_re, "im_divby_re"); + else + res_im = lhs_im; + } + // if divisor is only imaginary, division is simple too + else if (!rhs_re && rhs_im) { + if (lhs_re) + res_im = gIR->ir->CreateFNeg( + gIR->ir->CreateFDiv(lhs_re, rhs_im, "re_divby_im"), "neg"); + else + res_im = lhs_re; + if (lhs_im) + res_re = gIR->ir->CreateFDiv(lhs_im, rhs_im, "im_divby_im"); + else + res_re = lhs_im; + } + // full division + else { + llvm::Value *tmp1, *tmp2, *denom; + + if (lhs_re && lhs_im) { + tmp1 = gIR->ir->CreateFMul(lhs_re, rhs_re, "rere"); + tmp2 = gIR->ir->CreateFMul(lhs_im, rhs_im, "imim"); + res_re = gIR->ir->CreateFAdd(tmp1, tmp2, "rere_plus_imim"); + + tmp1 = gIR->ir->CreateFMul(lhs_re, rhs_im, "reim"); + tmp2 = gIR->ir->CreateFMul(lhs_im, rhs_re, "imre"); + res_im = gIR->ir->CreateFSub(tmp2, tmp1, "imre_sub_reim"); + } else if (lhs_re) { + res_re = gIR->ir->CreateFMul(lhs_re, rhs_re, "rere"); + + res_im = gIR->ir->CreateFMul(lhs_re, rhs_im, "reim"); + res_im = gIR->ir->CreateFNeg(res_im); + } else if (lhs_im) { + res_re = gIR->ir->CreateFMul(lhs_im, rhs_im, "imim"); + res_im = gIR->ir->CreateFMul(lhs_im, rhs_re, "imre"); + } else + llvm_unreachable("lhs has neither real nor imaginary part"); + + tmp1 = gIR->ir->CreateFMul(rhs_re, rhs_re, "rhs_resq"); + tmp2 = gIR->ir->CreateFMul(rhs_im, rhs_im, "rhs_imsq"); + denom = gIR->ir->CreateFAdd(tmp1, tmp2, "denom"); + + res_re = gIR->ir->CreateFDiv(res_re, denom, "res_re"); + res_im = gIR->ir->CreateFDiv(res_im, denom, "res_im"); + } + + LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im); + return new DImValue(type, res); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DValue *DtoComplexRem(Loc &loc, Type *type, DValue *lhs, DValue *rhs) { + llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im, *divisor; + + // lhs values + DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); + // rhs values + DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); + + // Divisor can be real or imaginary but not complex + assert((rhs_re != 0) ^ (rhs_im != 0)); + + divisor = rhs_re ? rhs_re : rhs_im; + res_re = lhs_re ? gIR->ir->CreateFRem(lhs_re, divisor) : lhs_re; + res_im = lhs_re ? gIR->ir->CreateFRem(lhs_im, divisor) : lhs_im; + + LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im); + return new DImValue(type, res); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DValue *DtoComplexNeg(Loc &loc, Type *type, DValue *val) { + llvm::Value *a, *b, *re, *im; + + // values + DtoGetComplexParts(loc, type, val, a, b); + + // neg up + assert(a && b); + re = gIR->ir->CreateFNeg(a); + im = gIR->ir->CreateFNeg(b); + + LLValue *res = DtoAggrPair(DtoType(type), re, im); + return new DImValue(type, res); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLValue *DtoComplexEquals(Loc &loc, TOK op, DValue *lhs, DValue *rhs) { + Type *type = lhs->getType(); + + DValue *lhs_re, *lhs_im, *rhs_re, *rhs_im; + + // lhs values + DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im); + // rhs values + DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im); + + // (l.re==r.re && l.im==r.im) or (l.re!=r.re || l.im!=r.im) + LLValue *b1 = DtoBinFloatsEquals(loc, lhs_re, rhs_re, op); + LLValue *b2 = DtoBinFloatsEquals(loc, lhs_im, rhs_im, op); + + if (op == TOKequal) + return gIR->ir->CreateAnd(b1, b2); + else + return gIR->ir->CreateOr(b1, b2); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DValue *DtoCastComplex(Loc &loc, DValue *val, Type *_to) { + Type *to = _to->toBasetype(); + Type *vty = val->getType()->toBasetype(); + if (to->iscomplex()) { + if (vty->size() == to->size()) + return val; + + llvm::Value *re, *im; + DtoGetComplexParts(loc, val->getType(), val, re, im); + LLType *toty = DtoComplexBaseType(to); + + if (to->size() < vty->size()) { + re = gIR->ir->CreateFPTrunc(re, toty); + im = gIR->ir->CreateFPTrunc(im, toty); + } else { + re = gIR->ir->CreateFPExt(re, toty); + im = gIR->ir->CreateFPExt(im, toty); + } + + LLValue *pair = DtoAggrPair(DtoType(_to), re, im); + return new DImValue(_to, pair); + } else if (to->isimaginary()) { + // FIXME: this loads both values, even when we only need one + LLValue *v = val->getRVal(); + LLValue *impart = gIR->ir->CreateExtractValue(v, 1, ".im_part"); + Type *extractty; + switch (vty->ty) { + default: + llvm_unreachable("Unexpected complex floating point type"); + case Tcomplex32: + extractty = Type::timaginary32; + break; + case Tcomplex64: + extractty = Type::timaginary64; + break; + case Tcomplex80: + extractty = Type::timaginary80; + break; + } + DImValue *im = new DImValue(extractty, impart); + return DtoCastFloat(loc, im, to); + } else if (to->ty == Tbool) { + return new DImValue( + _to, DtoComplexEquals(loc, TOKnotequal, val, DtoNullValue(vty))); + } else if (to->isfloating() || to->isintegral()) { + // FIXME: this loads both values, even when we only need one + LLValue *v = val->getRVal(); + LLValue *repart = gIR->ir->CreateExtractValue(v, 0, ".re_part"); + Type *extractty; + switch (vty->ty) { + default: + llvm_unreachable("Unexpected complex floating point type"); + case Tcomplex32: + extractty = Type::tfloat32; + break; + case Tcomplex64: + extractty = Type::tfloat64; + break; + case Tcomplex80: + extractty = Type::tfloat80; + break; + } + DImValue *re = new DImValue(extractty, repart); + return DtoCastFloat(loc, re, to); + } else { + error(loc, "Don't know how to cast %s to %s", vty->toChars(), + to->toChars()); + fatal(); + } +} diff --git a/gen/complex.h b/gen/complex.h index 9d385f4f7c..3592e324d2 100644 --- a/gen/complex.h +++ b/gen/complex.h @@ -20,37 +20,38 @@ class DValue; struct Loc; class Type; -namespace llvm -{ - class Constant; - class StructType; - class Type; - class Value; +namespace llvm { +class Constant; +class StructType; +class Type; +class Value; } -llvm::StructType* DtoComplexType(Type* t); -llvm::Type* DtoComplexBaseType(Type* t); +llvm::StructType *DtoComplexType(Type *t); +llvm::Type *DtoComplexBaseType(Type *t); -llvm::Constant* DtoConstComplex(Type* t, longdouble re, longdouble im); +llvm::Constant *DtoConstComplex(Type *t, longdouble re, longdouble im); -llvm::Constant* DtoComplexShuffleMask(unsigned a, unsigned b); +llvm::Constant *DtoComplexShuffleMask(unsigned a, unsigned b); -DValue* DtoComplex(Loc& loc, Type* to, DValue* val); +DValue *DtoComplex(Loc &loc, Type *to, DValue *val); -void DtoComplexSet(llvm::Value* c, llvm::Value* re, llvm::Value* im); +void DtoComplexSet(llvm::Value *c, llvm::Value *re, llvm::Value *im); -void DtoGetComplexParts(Loc& loc, Type* to, DValue* c, DValue*& re, DValue*& im); -void DtoGetComplexParts(Loc& loc, Type* to, DValue* c, llvm::Value*& re, llvm::Value*& im); +void DtoGetComplexParts(Loc &loc, Type *to, DValue *c, DValue *&re, + DValue *&im); +void DtoGetComplexParts(Loc &loc, Type *to, DValue *c, llvm::Value *&re, + llvm::Value *&im); -DValue* DtoComplexAdd(Loc& loc, Type* type, DValue* lhs, DValue* rhs); -DValue* DtoComplexSub(Loc& loc, Type* type, DValue* lhs, DValue* rhs); -DValue* DtoComplexMul(Loc& loc, Type* type, DValue* lhs, DValue* rhs); -DValue* DtoComplexDiv(Loc& loc, Type* type, DValue* lhs, DValue* rhs); -DValue* DtoComplexRem(Loc& loc, Type* type, DValue* lhs, DValue* rhs); -DValue* DtoComplexNeg(Loc& loc, Type* type, DValue* val); +DValue *DtoComplexAdd(Loc &loc, Type *type, DValue *lhs, DValue *rhs); +DValue *DtoComplexSub(Loc &loc, Type *type, DValue *lhs, DValue *rhs); +DValue *DtoComplexMul(Loc &loc, Type *type, DValue *lhs, DValue *rhs); +DValue *DtoComplexDiv(Loc &loc, Type *type, DValue *lhs, DValue *rhs); +DValue *DtoComplexRem(Loc &loc, Type *type, DValue *lhs, DValue *rhs); +DValue *DtoComplexNeg(Loc &loc, Type *type, DValue *val); -llvm::Value* DtoComplexEquals(Loc& loc, TOK op, DValue* lhs, DValue* rhs); +llvm::Value *DtoComplexEquals(Loc &loc, TOK op, DValue *lhs, DValue *rhs); -DValue* DtoCastComplex(Loc& loc, DValue* val, Type* to); +DValue *DtoCastComplex(Loc &loc, DValue *val, Type *to); #endif // LDC_GEN_COMPLEX_H diff --git a/gen/coverage.cpp b/gen/coverage.cpp index bef1df98dd..7566d1fbe2 100644 --- a/gen/coverage.cpp +++ b/gen/coverage.cpp @@ -15,40 +15,38 @@ #include "gen/logger.h" void emitCoverageLinecountInc(Loc &loc) { - // Only emit coverage increment for locations in the source of the current module - // (for example, 'inlined' methods from other source files should be skipped). - if (!global.params.cov || !loc.linnum || !loc.filename || - strcmp(gIR->dmodule->srcfile->name->toChars(), loc.filename) != 0) { - return; - } + // Only emit coverage increment for locations in the source of the current + // module + // (for example, 'inlined' methods from other source files should be skipped). + if (!global.params.cov || !loc.linnum || !loc.filename || + strcmp(gIR->dmodule->srcfile->name->toChars(), loc.filename) != 0) { + return; + } - const unsigned line = loc.linnum - 1; // convert to 0-based line# index - assert(line < gIR->dmodule->numlines); + const unsigned line = loc.linnum - 1; // convert to 0-based line# index + assert(line < gIR->dmodule->numlines); - IF_LOG Logger::println("Coverage: increment _d_cover_data[%d]", line); - LOG_SCOPE; + IF_LOG Logger::println("Coverage: increment _d_cover_data[%d]", line); + LOG_SCOPE; - // Get GEP into _d_cover_data array - LLConstant* idxs[] = { DtoConstUint(0), DtoConstUint(line) }; - LLValue* ptr = llvm::ConstantExpr::getGetElementPtr( + // Get GEP into _d_cover_data array + LLConstant *idxs[] = {DtoConstUint(0), DtoConstUint(line)}; + LLValue *ptr = llvm::ConstantExpr::getGetElementPtr( #if LDC_LLVM_VER >= 307 - LLArrayType::get(LLType::getInt32Ty(gIR->context()), gIR->dmodule->numlines), + LLArrayType::get(LLType::getInt32Ty(gIR->context()), + gIR->dmodule->numlines), #endif - gIR->dmodule->d_cover_data, idxs, true); + gIR->dmodule->d_cover_data, idxs, true); - // Do an atomic increment, so this works when multiple threads are executed. - gIR->ir->CreateAtomicRMW( - llvm::AtomicRMWInst::Add, - ptr, - DtoConstUint(1), - llvm::Monotonic - ); + // Do an atomic increment, so this works when multiple threads are executed. + gIR->ir->CreateAtomicRMW(llvm::AtomicRMWInst::Add, ptr, DtoConstUint(1), + llvm::Monotonic); - unsigned num_sizet_bits = gDataLayout->getTypeSizeInBits(DtoSize_t()); - unsigned idx = line / num_sizet_bits; - unsigned bitidx = line % num_sizet_bits; + unsigned num_sizet_bits = gDataLayout->getTypeSizeInBits(DtoSize_t()); + unsigned idx = line / num_sizet_bits; + unsigned bitidx = line % num_sizet_bits; - IF_LOG Logger::println("_d_cover_valid[%d] |= (1 << %d)", idx, bitidx); + IF_LOG Logger::println("_d_cover_valid[%d] |= (1 << %d)", idx, bitidx); - gIR->dmodule->d_cover_valid_init[idx] |= (size_t(1) << bitidx); + gIR->dmodule->d_cover_valid_init[idx] |= (size_t(1) << bitidx); } diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 3f46334cc4..6a2856c853 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -27,523 +27,510 @@ ////////////////////////////////////////////////////////////////////////////// // FIXME: Integrate these functions -void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState* p); -void TypeInfoClassDeclaration_codegen(TypeInfoDeclaration *decl, IRState* p); +void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p); +void TypeInfoClassDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p); ////////////////////////////////////////////////////////////////////////////// -namespace -{ +namespace { // from dmd/src/typinf.c -bool isSpeculativeType(Type *t) -{ - class SpeculativeTypeVisitor : public Visitor - { - public: - bool result; +bool isSpeculativeType(Type *t) { + class SpeculativeTypeVisitor : public Visitor { + public: + bool result; - SpeculativeTypeVisitor() : result(false) {} + SpeculativeTypeVisitor() : result(false) {} - void visit(Type *t) - { - Type *tb = t->toBasetype(); - if (tb != t) - tb->accept(this); - } - void visit(TypeNext *t) - { - if (t->next) - t->next->accept(this); - } - void visit(TypeBasic *t) { } - void visit(TypeVector *t) - { - t->basetype->accept(this); - } - void visit(TypeAArray *t) - { - t->index->accept(this); - visit((TypeNext *)t); - } - void visit(TypeFunction *t) - { - visit((TypeNext *)t); - // Currently TypeInfo_Function doesn't store parameter types. - } - void visit(TypeStruct *t) - { - StructDeclaration *sd = t->sym; - if (TemplateInstance *ti = sd->isInstantiated()) - { - if (!ti->needsCodegen()) - { - if (ti->minst || sd->requestTypeInfo) - return; + void visit(Type *t) { + Type *tb = t->toBasetype(); + if (tb != t) + tb->accept(this); + } + void visit(TypeNext *t) { + if (t->next) + t->next->accept(this); + } + void visit(TypeBasic *t) {} + void visit(TypeVector *t) { t->basetype->accept(this); } + void visit(TypeAArray *t) { + t->index->accept(this); + visit((TypeNext *)t); + } + void visit(TypeFunction *t) { + visit((TypeNext *)t); + // Currently TypeInfo_Function doesn't store parameter types. + } + void visit(TypeStruct *t) { + StructDeclaration *sd = t->sym; + if (TemplateInstance *ti = sd->isInstantiated()) { + if (!ti->needsCodegen()) { + if (ti->minst || sd->requestTypeInfo) + return; - /* Bugzilla 14425: TypeInfo_Struct would refer the members of - * struct (e.g. opEquals via xopEquals field), so if it's instantiated - * in speculative context, TypeInfo creation should also be - * stopped to avoid 'unresolved symbol' linker errors. - */ - /* When -debug/-unittest is specified, all of non-root instances are - * automatically changed to speculative, and here is always reached - * from those instantiated non-root structs. - * Therefore, if the TypeInfo is not auctually requested, - * we have to elide its codegen. - */ - result |= true; - return; - } - } - else - { - //assert(!sd->inNonRoot() || sd->requestTypeInfo); // valid? - } + /* Bugzilla 14425: TypeInfo_Struct would refer the members of + * struct (e.g. opEquals via xopEquals field), so if it's instantiated + * in speculative context, TypeInfo creation should also be + * stopped to avoid 'unresolved symbol' linker errors. + */ + /* When -debug/-unittest is specified, all of non-root instances are + * automatically changed to speculative, and here is always reached + * from those instantiated non-root structs. + * Therefore, if the TypeInfo is not auctually requested, + * we have to elide its codegen. + */ + result |= true; + return; } - void visit(TypeClass *t) { } - void visit(TypeTuple *t) - { - if (t->arguments) - { - for (size_t i = 0; i < t->arguments->dim; i++) - { - Type *tprm = (*t->arguments)[i]->type; - if (tprm) - tprm->accept(this); - if (result) - return; - } - } + } else { + // assert(!sd->inNonRoot() || sd->requestTypeInfo); // valid? + } + } + void visit(TypeClass *t) {} + void visit(TypeTuple *t) { + if (t->arguments) { + for (size_t i = 0; i < t->arguments->dim; i++) { + Type *tprm = (*t->arguments)[i]->type; + if (tprm) + tprm->accept(this); + if (result) + return; } - }; - SpeculativeTypeVisitor v; - t->accept(&v); - return v.result; + } + } + }; + SpeculativeTypeVisitor v; + t->accept(&v); + return v.result; } } - ////////////////////////////////////////////////////////////////////////////// class CodegenVisitor : public Visitor { - IRState *irs; + IRState *irs; + public: + CodegenVisitor(IRState *irs) : irs(irs) {} - CodegenVisitor(IRState *irs) : irs(irs) { } + ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// + // Import all functions from class Visitor + using Visitor::visit; - // Import all functions from class Visitor - using Visitor::visit; + ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// + void visit(Dsymbol *sym) LLVM_OVERRIDE { + IF_LOG Logger::println("Ignoring Dsymbol::codegen for %s", + sym->toPrettyChars()); + } - void visit(Dsymbol *sym) LLVM_OVERRIDE { - IF_LOG Logger::println("Ignoring Dsymbol::codegen for %s", sym->toPrettyChars()); + ////////////////////////////////////////////////////////////////////////// + + void visit(InterfaceDeclaration *decl) LLVM_OVERRIDE { + IF_LOG Logger::println("InterfaceDeclaration::codegen: '%s'", + decl->toPrettyChars()); + LOG_SCOPE + + if (decl->ir.isDefined()) + return; + + if (decl->type->ty == Terror) { + error(decl->loc, "had semantic errors when compiling"); + decl->ir.setDefined(); + return; } - ////////////////////////////////////////////////////////////////////////// + if (decl->members && decl->symtab) { + DtoResolveClass(decl); + decl->ir.setDefined(); - void visit(InterfaceDeclaration *decl) LLVM_OVERRIDE { - IF_LOG Logger::println("InterfaceDeclaration::codegen: '%s'", decl->toPrettyChars()); - LOG_SCOPE + // Emit any members (e.g. final functions). + for (auto m : *decl->members) + m->accept(this); - if (decl->ir.isDefined()) return; + // Emit TypeInfo. + DtoTypeInfoOf(decl->type); - if (decl->type->ty == Terror) - { - error(decl->loc, "had semantic errors when compiling"); - decl->ir.setDefined(); - return; - } + // Define __InterfaceZ. + IrAggr *ir = getIrAggr(decl); + llvm::GlobalVariable *interfaceZ = ir->getClassInfoSymbol(); + interfaceZ->setInitializer(ir->getClassInfoInit()); + LinkageWithCOMDAT lwc = DtoLinkage(decl); + interfaceZ->setLinkage(lwc.first); + if (lwc.second) + SET_COMDAT(interfaceZ, gIR->module); + } + } - if (decl->members && decl->symtab) - { - DtoResolveClass(decl); - decl->ir.setDefined(); + ////////////////////////////////////////////////////////////////////////// - // Emit any members (e.g. final functions). - for (auto m : *decl->members) - m->accept(this); + void visit(StructDeclaration *decl) LLVM_OVERRIDE { + IF_LOG Logger::println("StructDeclaration::codegen: '%s'", + decl->toPrettyChars()); + LOG_SCOPE - // Emit TypeInfo. - DtoTypeInfoOf(decl->type); + if (decl->ir.isDefined()) + return; - // Define __InterfaceZ. - IrAggr *ir = getIrAggr(decl); - llvm::GlobalVariable *interfaceZ = ir->getClassInfoSymbol(); - interfaceZ->setInitializer(ir->getClassInfoInit()); - LinkageWithCOMDAT lwc = DtoLinkage(decl); - interfaceZ->setLinkage(lwc.first); - if (lwc.second) SET_COMDAT(interfaceZ, gIR->module); - } + if (decl->type->ty == Terror) { + error(decl->loc, "had semantic errors when compiling"); + decl->ir.setDefined(); + return; } - ////////////////////////////////////////////////////////////////////////// + if (decl->members && decl->symtab) { + DtoResolveStruct(decl); + decl->ir.setDefined(); - void visit(StructDeclaration *decl) LLVM_OVERRIDE { - IF_LOG Logger::println("StructDeclaration::codegen: '%s'", decl->toPrettyChars()); - LOG_SCOPE + for (auto m : *decl->members) + m->accept(this); - if (decl->ir.isDefined()) return; + // Define the __initZ symbol. + IrAggr *ir = getIrAggr(decl); + llvm::GlobalVariable *initZ = ir->getInitSymbol(); + initZ->setInitializer(ir->getDefaultInit()); + LinkageWithCOMDAT lwc = DtoLinkage(decl); + initZ->setLinkage(lwc.first); + if (lwc.second) + SET_COMDAT(initZ, gIR->module); - if (decl->type->ty == Terror) - { - error(decl->loc, "had semantic errors when compiling"); - decl->ir.setDefined(); - return; - } + // emit typeinfo + DtoTypeInfoOf(decl->type); - if (decl->members && decl->symtab) - { - DtoResolveStruct(decl); - decl->ir.setDefined(); + // Emit __xopEquals/__xopCmp/__xtoHash. + if (decl->xeq && decl->xeq != decl->xerreq) + decl->xeq->accept(this); + if (decl->xcmp && decl->xcmp != decl->xerrcmp) + decl->xcmp->accept(this); + if (decl->xhash) + decl->xhash->accept(this); + } + } - for (auto m : *decl->members) - m->accept(this); + ////////////////////////////////////////////////////////////////////////// - // Define the __initZ symbol. - IrAggr *ir = getIrAggr(decl); - llvm::GlobalVariable *initZ = ir->getInitSymbol(); - initZ->setInitializer(ir->getDefaultInit()); - LinkageWithCOMDAT lwc = DtoLinkage(decl); - initZ->setLinkage(lwc.first); - if (lwc.second) SET_COMDAT(initZ, gIR->module); + void visit(ClassDeclaration *decl) LLVM_OVERRIDE { + IF_LOG Logger::println("ClassDeclaration::codegen: '%s'", + decl->toPrettyChars()); + LOG_SCOPE - // emit typeinfo - DtoTypeInfoOf(decl->type); + if (decl->ir.isDefined()) + return; - // Emit __xopEquals/__xopCmp/__xtoHash. - if (decl->xeq && decl->xeq != decl->xerreq) - decl->xeq->accept(this); - if (decl->xcmp && decl->xcmp != decl->xerrcmp) - decl->xcmp->accept(this); - if (decl->xhash) - decl->xhash->accept(this); - } + if (decl->type->ty == Terror) { + error(decl->loc, "had semantic errors when compiling"); + decl->ir.setDefined(); + return; } - ////////////////////////////////////////////////////////////////////////// + if (decl->members && decl->symtab) { + DtoResolveClass(decl); + decl->ir.setDefined(); - void visit(ClassDeclaration *decl) LLVM_OVERRIDE { - IF_LOG Logger::println("ClassDeclaration::codegen: '%s'", decl->toPrettyChars()); - LOG_SCOPE + for (auto m : *decl->members) + m->accept(this); - if (decl->ir.isDefined()) return; + IrAggr *ir = getIrAggr(decl); + const LinkageWithCOMDAT lwc = DtoLinkage(decl); - if (decl->type->ty == Terror) - { - error(decl->loc, "had semantic errors when compiling"); - decl->ir.setDefined(); - return; - } + llvm::GlobalVariable *initZ = ir->getInitSymbol(); + initZ->setInitializer(ir->getDefaultInit()); + initZ->setLinkage(lwc.first); + if (lwc.second) + SET_COMDAT(initZ, gIR->module); - if (decl->members && decl->symtab) - { - DtoResolveClass(decl); - decl->ir.setDefined(); + llvm::GlobalVariable *vtbl = ir->getVtblSymbol(); + vtbl->setInitializer(ir->getVtblInit()); + vtbl->setLinkage(lwc.first); + if (lwc.second) + SET_COMDAT(vtbl, gIR->module); - for (auto m : *decl->members) - m->accept(this); + llvm::GlobalVariable *classZ = ir->getClassInfoSymbol(); + classZ->setInitializer(ir->getClassInfoInit()); + classZ->setLinkage(lwc.first); + if (lwc.second) + SET_COMDAT(classZ, gIR->module); - IrAggr *ir = getIrAggr(decl); - const LinkageWithCOMDAT lwc = DtoLinkage(decl); + // No need to do TypeInfo here, it is __classZ for classes in D2. + } + } - llvm::GlobalVariable *initZ = ir->getInitSymbol(); - initZ->setInitializer(ir->getDefaultInit()); - initZ->setLinkage(lwc.first); - if (lwc.second) SET_COMDAT(initZ, gIR->module); + ////////////////////////////////////////////////////////////////////////// - llvm::GlobalVariable *vtbl = ir->getVtblSymbol(); - vtbl->setInitializer(ir->getVtblInit()); - vtbl->setLinkage(lwc.first); - if (lwc.second) SET_COMDAT(vtbl, gIR->module); + void visit(TupleDeclaration *decl) LLVM_OVERRIDE { + IF_LOG Logger::println("TupleDeclaration::codegen(): '%s'", + decl->toPrettyChars()); + LOG_SCOPE - llvm::GlobalVariable *classZ = ir->getClassInfoSymbol(); - classZ->setInitializer(ir->getClassInfoInit()); - classZ->setLinkage(lwc.first); - if (lwc.second) SET_COMDAT(classZ, gIR->module); + if (decl->ir.isDefined()) + return; + decl->ir.setDefined(); - // No need to do TypeInfo here, it is __classZ for classes in D2. - } + assert(decl->isexp); + assert(decl->objects); + + for (auto o : *decl->objects) { + DsymbolExp *exp = static_cast(o); + assert(exp->op == TOKdsymbol); + exp->s->accept(this); + } + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(VarDeclaration *decl) LLVM_OVERRIDE { + IF_LOG Logger::println("VarDeclaration::codegen(): '%s'", + decl->toPrettyChars()); + LOG_SCOPE; + + if (decl->ir.isDefined()) + return; + + if (decl->type->ty == Terror) { + error(decl->loc, "had semantic errors when compiling"); + decl->ir.setDefined(); + return; } - ////////////////////////////////////////////////////////////////////////// + DtoResolveVariable(decl); + decl->ir.setDefined(); - void visit(TupleDeclaration *decl) LLVM_OVERRIDE { - IF_LOG Logger::println("TupleDeclaration::codegen(): '%s'", decl->toPrettyChars()); - LOG_SCOPE - - if (decl->ir.isDefined()) return; - decl->ir.setDefined(); - - assert(decl->isexp); - assert(decl->objects); - - for (auto o : *decl->objects) - { - DsymbolExp *exp = static_cast(o); - assert(exp->op == TOKdsymbol); - exp->s->accept(this); - } + // just forward aliases + if (decl->aliassym) { + Logger::println("alias sym"); + decl->toAlias()->accept(this); + return; } - ////////////////////////////////////////////////////////////////////////// + // global variable + if (decl->isDataseg()) { + Logger::println("data segment"); - void visit(VarDeclaration *decl) LLVM_OVERRIDE { - IF_LOG Logger::println("VarDeclaration::codegen(): '%s'", decl->toPrettyChars()); - LOG_SCOPE; + assert(!(decl->storage_class & STCmanifest) && + "manifest constant being codegen'd!"); - if (decl->ir.isDefined()) return; + IrGlobal *irGlobal = getIrGlobal(decl); + llvm::GlobalVariable *gvar = + llvm::cast(irGlobal->value); + assert(gvar && "DtoResolveVariable should have created value"); - if (decl->type->ty == Terror) - { - error(decl->loc, "had semantic errors when compiling"); - decl->ir.setDefined(); - return; + const LinkageWithCOMDAT lwc = DtoLinkage(decl); + + // Check if we are defining or just declaring the global in this module. + if (!(decl->storage_class & STCextern)) { + // Build the initializer. Might use this->ir.irGlobal->value! + LLConstant *initVal = + DtoConstInitializer(decl->loc, decl->type, decl->init); + + // In case of type mismatch, swap out the variable. + if (initVal->getType() != gvar->getType()->getElementType()) { + llvm::GlobalVariable *newGvar = getOrCreateGlobal( + decl->loc, irs->module, initVal->getType(), gvar->isConstant(), + lwc.first, 0, + "", // We take on the name of the old global below. + gvar->isThreadLocal()); + if (lwc.second) + SET_COMDAT(newGvar, gIR->module); + + newGvar->setAlignment(gvar->getAlignment()); + newGvar->takeName(gvar); + + llvm::Constant *newValue = + llvm::ConstantExpr::getBitCast(newGvar, gvar->getType()); + gvar->replaceAllUsesWith(newValue); + + gvar->eraseFromParent(); + gvar = newGvar; + irGlobal->value = newGvar; } - DtoResolveVariable(decl); - decl->ir.setDefined(); + // Now, set the initializer. + assert(!irGlobal->constInit); + irGlobal->constInit = initVal; + gvar->setInitializer(initVal); + gvar->setLinkage(lwc.first); + if (lwc.second) + SET_COMDAT(gvar, gIR->module); - // just forward aliases - if (decl->aliassym) - { - Logger::println("alias sym"); - decl->toAlias()->accept(this); - return; - } + // Also set up the debug info. + irs->DBuilder.EmitGlobalVariable(gvar, decl); + } - // global variable - if (decl->isDataseg()) - { - Logger::println("data segment"); + // If this global is used from a naked function, we need to create an + // artificial "use" for it, or it could be removed by the optimizer if + // the only reference to it is in inline asm. + if (irGlobal->nakedUse) + irs->usedArray.push_back(DtoBitCast(gvar, getVoidPtrType())); - assert(!(decl->storage_class & STCmanifest) && - "manifest constant being codegen'd!"); - - IrGlobal *irGlobal = getIrGlobal(decl); - llvm::GlobalVariable *gvar = llvm::cast(irGlobal->value); - assert(gvar && "DtoResolveVariable should have created value"); - - const LinkageWithCOMDAT lwc = DtoLinkage(decl); - - // Check if we are defining or just declaring the global in this module. - if (!(decl->storage_class & STCextern)) - { - // Build the initializer. Might use this->ir.irGlobal->value! - LLConstant *initVal = DtoConstInitializer(decl->loc, decl->type, decl->init); - - // In case of type mismatch, swap out the variable. - if (initVal->getType() != gvar->getType()->getElementType()) - { - llvm::GlobalVariable* newGvar = getOrCreateGlobal(decl->loc, - irs->module, initVal->getType(), gvar->isConstant(), - lwc.first, 0, - "", // We take on the name of the old global below. - gvar->isThreadLocal()); - if (lwc.second) SET_COMDAT(newGvar, gIR->module); - - newGvar->setAlignment(gvar->getAlignment()); - newGvar->takeName(gvar); - - llvm::Constant* newValue = - llvm::ConstantExpr::getBitCast(newGvar, gvar->getType()); - gvar->replaceAllUsesWith(newValue); - - gvar->eraseFromParent(); - gvar = newGvar; - irGlobal->value = newGvar; - } - - // Now, set the initializer. - assert(!irGlobal->constInit); - irGlobal->constInit = initVal; - gvar->setInitializer(initVal); - gvar->setLinkage(lwc.first); - if (lwc.second) SET_COMDAT(gvar, gIR->module); - - // Also set up the debug info. - irs->DBuilder.EmitGlobalVariable(gvar, decl); - } - - // If this global is used from a naked function, we need to create an - // artificial "use" for it, or it could be removed by the optimizer if - // the only reference to it is in inline asm. - if (irGlobal->nakedUse) - irs->usedArray.push_back(DtoBitCast(gvar, getVoidPtrType())); - - IF_LOG Logger::cout() << *gvar << '\n'; - } + IF_LOG Logger::cout() << *gvar << '\n'; } + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(EnumDeclaration *decl) LLVM_OVERRIDE { - IF_LOG Logger::println("Ignoring EnumDeclaration::codegen: '%s'", decl->toPrettyChars()); + void visit(EnumDeclaration *decl) LLVM_OVERRIDE { + IF_LOG Logger::println("Ignoring EnumDeclaration::codegen: '%s'", + decl->toPrettyChars()); - if (decl->type->ty == Terror) - { error(decl->loc, "had semantic errors when compiling"); - return; - } + if (decl->type->ty == Terror) { + error(decl->loc, "had semantic errors when compiling"); + return; } + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(FuncDeclaration *decl) LLVM_OVERRIDE { - // don't touch function aliases, they don't contribute any new symbols - if (!decl->isFuncAliasDeclaration()) - { - DtoDefineFunction(decl); - } + void visit(FuncDeclaration *decl) LLVM_OVERRIDE { + // don't touch function aliases, they don't contribute any new symbols + if (!decl->isFuncAliasDeclaration()) { + DtoDefineFunction(decl); } + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(TemplateInstance *decl) LLVM_OVERRIDE { - IF_LOG Logger::println("TemplateInstance::codegen: '%s'", decl->toPrettyChars()); - LOG_SCOPE + void visit(TemplateInstance *decl) LLVM_OVERRIDE { + IF_LOG Logger::println("TemplateInstance::codegen: '%s'", + decl->toPrettyChars()); + LOG_SCOPE - if (decl->ir.isDefined()) return; - decl->ir.setDefined(); + if (decl->ir.isDefined()) + return; + decl->ir.setDefined(); - // FIXME: This is #673 all over again. - if (!decl->needsCodegen()) return; + // FIXME: This is #673 all over again. + if (!decl->needsCodegen()) + return; - if (!isError(decl) && decl->members) - { - for (auto m : *decl->members) - m->accept(this); - } + if (!isError(decl) && decl->members) { + for (auto m : *decl->members) + m->accept(this); } + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(TemplateMixin *decl) LLVM_OVERRIDE { - IF_LOG Logger::println("TemplateInstance::codegen: '%s'", decl->toPrettyChars()); - LOG_SCOPE + void visit(TemplateMixin *decl) LLVM_OVERRIDE { + IF_LOG Logger::println("TemplateInstance::codegen: '%s'", + decl->toPrettyChars()); + LOG_SCOPE - if (decl->ir.isDefined()) return; - decl->ir.setDefined(); + if (decl->ir.isDefined()) + return; + decl->ir.setDefined(); - if (!isError(decl) && decl->members) - { - for (auto m : *decl->members) - m->accept(this); - } + if (!isError(decl) && decl->members) { + for (auto m : *decl->members) + m->accept(this); } + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(AttribDeclaration *decl) LLVM_OVERRIDE { - Dsymbols *d = decl->include(NULL, NULL); + void visit(AttribDeclaration *decl) LLVM_OVERRIDE { + Dsymbols *d = decl->include(NULL, NULL); - if (d) - { - for (auto s : *d) - s->accept(this); - } + if (d) { + for (auto s : *d) + s->accept(this); } + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(PragmaDeclaration *decl) LLVM_OVERRIDE { - if (decl->ident == Id::lib) - { - assert(decl->args && decl->args->dim == 1); + void visit(PragmaDeclaration *decl) LLVM_OVERRIDE { + if (decl->ident == Id::lib) { + assert(decl->args && decl->args->dim == 1); - Expression *e = static_cast(decl->args->data[0]); + Expression *e = static_cast(decl->args->data[0]); - assert(e->op == TOKstring); - StringExp *se = static_cast(e); + assert(e->op == TOKstring); + StringExp *se = static_cast(e); - size_t nameLen = se->len; - if (global.params.targetTriple.isWindowsGNUEnvironment()) - { - if (nameLen > 4 && - !memcmp(static_cast(se->string) + nameLen - 4, ".lib", 4)) - { - // On MinGW, strip the .lib suffix, if any, to improve - // compatibility with code written for DMD (we pass the name to GCC - // via -l, just as on Posix). - nameLen -= 4; - } + size_t nameLen = se->len; + if (global.params.targetTriple.isWindowsGNUEnvironment()) { + if (nameLen > 4 && + !memcmp(static_cast(se->string) + nameLen - 4, ".lib", 4)) { + // On MinGW, strip the .lib suffix, if any, to improve + // compatibility with code written for DMD (we pass the name to GCC + // via -l, just as on Posix). + nameLen -= 4; + } - if (nameLen >= 7 && !memcmp(se->string, "shell32", 7)) - { - // Another DMD compatibility kludge: Ignore - // pragma(lib, "shell32.lib"), it is implicitly provided by - // MinGW. - return; - } - } + if (nameLen >= 7 && !memcmp(se->string, "shell32", 7)) { + // Another DMD compatibility kludge: Ignore + // pragma(lib, "shell32.lib"), it is implicitly provided by + // MinGW. + return; + } + } - // With LLVM 3.3 or later we can place the library name in the object - // file. This seems to be supported only on Windows. - if (global.params.targetTriple.isWindowsMSVCEnvironment()) - { - llvm::SmallString<24> LibName(llvm::StringRef(static_cast(se->string), nameLen)); + // With LLVM 3.3 or later we can place the library name in the object + // file. This seems to be supported only on Windows. + if (global.params.targetTriple.isWindowsMSVCEnvironment()) { + llvm::SmallString<24> LibName( + llvm::StringRef(static_cast(se->string), nameLen)); - // Win32: /DEFAULTLIB:"curl" - if (LibName.endswith(".a")) - LibName = LibName.substr(0, LibName.size()-2); - if (LibName.endswith(".lib")) - LibName = LibName.substr(0, LibName.size()-4); - llvm::SmallString<24> tmp("/DEFAULTLIB:\""); - tmp.append(LibName); - tmp.append("\""); - LibName = tmp; + // Win32: /DEFAULTLIB:"curl" + if (LibName.endswith(".a")) + LibName = LibName.substr(0, LibName.size() - 2); + if (LibName.endswith(".lib")) + LibName = LibName.substr(0, LibName.size() - 4); + llvm::SmallString<24> tmp("/DEFAULTLIB:\""); + tmp.append(LibName); + tmp.append("\""); + LibName = tmp; - // Embedd library name as linker option in object file +// Embedd library name as linker option in object file #if LDC_LLVM_VER >= 306 - llvm::Metadata *Value = llvm::MDString::get(gIR->context(), LibName); - gIR->LinkerMetadataArgs.push_back(llvm::MDNode::get(gIR->context(), Value)); + llvm::Metadata *Value = llvm::MDString::get(gIR->context(), LibName); + gIR->LinkerMetadataArgs.push_back( + llvm::MDNode::get(gIR->context(), Value)); #else - llvm::Value *Value = llvm::MDString::get(gIR->context(), LibName); - gIR->LinkerMetadataArgs.push_back(llvm::MDNode::get(gIR->context(), Value)); + llvm::Value *Value = llvm::MDString::get(gIR->context(), LibName); + gIR->LinkerMetadataArgs.push_back( + llvm::MDNode::get(gIR->context(), Value)); #endif - } - else - { - size_t const n = nameLen + 3; - char *arg = static_cast(mem.xmalloc(n)); - arg[0] = '-'; - arg[1] = 'l'; - memcpy(arg + 2, se->string, nameLen); - arg[n-1] = 0; - global.params.linkswitches->push(arg); - } - } - visit(static_cast(decl)); + } else { + size_t const n = nameLen + 3; + char *arg = static_cast(mem.xmalloc(n)); + arg[0] = '-'; + arg[1] = 'l'; + memcpy(arg + 2, se->string, nameLen); + arg[n - 1] = 0; + global.params.linkswitches->push(arg); + } } + visit(static_cast(decl)); + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(TypeInfoDeclaration *decl) LLVM_OVERRIDE { - if (isSpeculativeType(decl->tinfo)) return; - TypeInfoDeclaration_codegen(decl, irs); - } + void visit(TypeInfoDeclaration *decl) LLVM_OVERRIDE { + if (isSpeculativeType(decl->tinfo)) + return; + TypeInfoDeclaration_codegen(decl, irs); + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(TypeInfoClassDeclaration *decl) LLVM_OVERRIDE { - if (isSpeculativeType(decl->tinfo)) return; - TypeInfoClassDeclaration_codegen(decl, irs); - } + void visit(TypeInfoClassDeclaration *decl) LLVM_OVERRIDE { + if (isSpeculativeType(decl->tinfo)) + return; + TypeInfoClassDeclaration_codegen(decl, irs); + } }; ////////////////////////////////////////////////////////////////////////////// -void Declaration_codegen(Dsymbol *decl) -{ - CodegenVisitor v(gIR); - decl->accept(&v); +void Declaration_codegen(Dsymbol *decl) { + CodegenVisitor v(gIR); + decl->accept(&v); } -void Declaration_codegen(Dsymbol *decl, IRState *irs) -{ - CodegenVisitor v(irs); - decl->accept(&v); +void Declaration_codegen(Dsymbol *decl, IRState *irs) { + CodegenVisitor v(irs); + decl->accept(&v); } ////////////////////////////////////////////////////////////////////////////// diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 54c6ca571b..3822420424 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -34,820 +34,765 @@ typedef llvm::DIDescriptor DIFlags; //////////////////////////////////////////////////////////////////////////////// -// get the module the symbol is in, or - for template instances - the current module -Module *ldc::DIBuilder::getDefinedModule(Dsymbol *s) -{ - // templates are defined in current module - if (DtoIsTemplateInstance(s)) - { - return IR->dmodule; - } - // array operations as well - else if (FuncDeclaration* fd = s->isFuncDeclaration()) - { - if (fd->isArrayOp && (willInline() || !isDruntimeArrayOp(fd))) - return IR->dmodule; - } - // otherwise use the symbol's module - return s->getModule(); +// get the module the symbol is in, or - for template instances - the current +// module +Module *ldc::DIBuilder::getDefinedModule(Dsymbol *s) { + // templates are defined in current module + if (DtoIsTemplateInstance(s)) { + return IR->dmodule; + } + // array operations as well + else if (FuncDeclaration *fd = s->isFuncDeclaration()) { + if (fd->isArrayOp && (willInline() || !isDruntimeArrayOp(fd))) + return IR->dmodule; + } + // otherwise use the symbol's module + return s->getModule(); } //////////////////////////////////////////////////////////////////////////////// ldc::DIBuilder::DIBuilder(IRState *const IR) - : IR(IR), DBuilder(IR->module), CUNode(0) -{ + : IR(IR), DBuilder(IR->module), CUNode(0) {} + +llvm::LLVMContext &ldc::DIBuilder::getContext() { return IR->context(); } + +ldc::DIScope ldc::DIBuilder::GetCurrentScope() { + IrFunction *fn = IR->func(); + if (fn->diLexicalBlocks.empty()) { + assert(static_cast(fn->diSubprogram) != 0); + return fn->diSubprogram; + } + return fn->diLexicalBlocks.top(); } -llvm::LLVMContext &ldc::DIBuilder::getContext() -{ - return IR->context(); -} - -ldc::DIScope ldc::DIBuilder::GetCurrentScope() -{ - IrFunction *fn = IR->func(); - if (fn->diLexicalBlocks.empty()) - { - assert(static_cast(fn->diSubprogram) != 0); - return fn->diSubprogram; - } - return fn->diLexicalBlocks.top(); -} - -void ldc::DIBuilder::Declare(const Loc &loc, llvm::Value *var, ldc::DILocalVariable divar +void ldc::DIBuilder::Declare(const Loc &loc, llvm::Value *var, + ldc::DILocalVariable divar #if LDC_LLVM_VER >= 306 - , ldc::DIExpression diexpr + , + ldc::DIExpression diexpr #endif - ) -{ - unsigned charnum = (loc.linnum ? loc.charnum : 0); - llvm::Instruction *instr = DBuilder.insertDeclare(var, divar, + ) { + unsigned charnum = (loc.linnum ? loc.charnum : 0); + llvm::Instruction *instr = DBuilder.insertDeclare( + var, divar, #if LDC_LLVM_VER >= 306 - diexpr, + diexpr, #endif #if LDC_LLVM_VER >= 307 - llvm::DebugLoc::get(loc.linnum, charnum, GetCurrentScope()), + llvm::DebugLoc::get(loc.linnum, charnum, GetCurrentScope()), #endif - IR->scopebb()); + IR->scopebb()); #if LDC_LLVM_VER < 307 - instr->setDebugLoc(llvm::DebugLoc::get(loc.linnum, charnum, GetCurrentScope())); + instr->setDebugLoc( + llvm::DebugLoc::get(loc.linnum, charnum, GetCurrentScope())); #endif } -ldc::DIFile ldc::DIBuilder::CreateFile(Loc& loc) -{ - llvm::SmallString<128> path(loc.filename ? loc.filename : ""); - llvm::sys::fs::make_absolute(path); +ldc::DIFile ldc::DIBuilder::CreateFile(Loc &loc) { + llvm::SmallString<128> path(loc.filename ? loc.filename : ""); + llvm::sys::fs::make_absolute(path); - return DBuilder.createFile( - llvm::sys::path::filename(path), - llvm::sys::path::parent_path(path) - ); + return DBuilder.createFile(llvm::sys::path::filename(path), + llvm::sys::path::parent_path(path)); } -ldc::DIType ldc::DIBuilder::CreateBasicType(Type *type) -{ - using namespace llvm::dwarf; +ldc::DIType ldc::DIBuilder::CreateBasicType(Type *type) { + using namespace llvm::dwarf; - Type *t = type->toBasetype(); - llvm::Type *T = DtoType(type); + Type *t = type->toBasetype(); + llvm::Type *T = DtoType(type); - // find encoding - unsigned Encoding; - switch (t->ty) - { - case Tbool: - Encoding = DW_ATE_boolean; - break; - case Tchar: - case Twchar: - case Tdchar: - Encoding = type->isunsigned() ? DW_ATE_unsigned_char - : DW_ATE_signed_char; - break; - case Tint8: - case Tint16: - case Tint32: - case Tint64: - case Tint128: - Encoding = DW_ATE_signed; - break; - case Tuns8: - case Tuns16: - case Tuns32: - case Tuns64: - case Tuns128: - Encoding = DW_ATE_unsigned; - break; - case Tfloat32: - case Tfloat64: - case Tfloat80: - Encoding = DW_ATE_float; - break; - case Timaginary32: - case Timaginary64: - case Timaginary80: - Encoding = DW_ATE_imaginary_float; - break; - case Tcomplex32: - case Tcomplex64: - case Tcomplex80: - Encoding = DW_ATE_complex_float; - break; - default: - llvm_unreachable("Unsupported basic type for debug info in DIBuilder::CreateBasicType"); - } + // find encoding + unsigned Encoding; + switch (t->ty) { + case Tbool: + Encoding = DW_ATE_boolean; + break; + case Tchar: + case Twchar: + case Tdchar: + Encoding = type->isunsigned() ? DW_ATE_unsigned_char : DW_ATE_signed_char; + break; + case Tint8: + case Tint16: + case Tint32: + case Tint64: + case Tint128: + Encoding = DW_ATE_signed; + break; + case Tuns8: + case Tuns16: + case Tuns32: + case Tuns64: + case Tuns128: + Encoding = DW_ATE_unsigned; + break; + case Tfloat32: + case Tfloat64: + case Tfloat80: + Encoding = DW_ATE_float; + break; + case Timaginary32: + case Timaginary64: + case Timaginary80: + Encoding = DW_ATE_imaginary_float; + break; + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + Encoding = DW_ATE_complex_float; + break; + default: + llvm_unreachable( + "Unsupported basic type for debug info in DIBuilder::CreateBasicType"); + } - return DBuilder.createBasicType( - type->toChars(), // name - getTypeBitSize(T), // size (bits) - getABITypeAlign(T)*8, // align (bits) - Encoding - ); + return DBuilder.createBasicType(type->toChars(), // name + getTypeBitSize(T), // size (bits) + getABITypeAlign(T) * 8, // align (bits) + Encoding); } -ldc::DIType ldc::DIBuilder::CreateEnumType(Type *type) -{ - llvm::Type *T = DtoType(type); +ldc::DIType ldc::DIBuilder::CreateEnumType(Type *type) { + llvm::Type *T = DtoType(type); - assert(type->ty == Tenum && "only enums allowed for debug info in dwarfEnumType"); - TypeEnum *te = static_cast(type); + assert(type->ty == Tenum && + "only enums allowed for debug info in dwarfEnumType"); + TypeEnum *te = static_cast(type); #if LDC_LLVM_VER >= 306 - llvm::SmallVector subscripts; + llvm::SmallVector subscripts; #else - llvm::SmallVector subscripts; + llvm::SmallVector subscripts; #endif - for (auto m : *te->sym->members) - { - EnumMember *em = m->isEnumMember(); - llvm::StringRef Name(em->toChars()); - uint64_t Val = em->value->toInteger(); - auto Subscript = DBuilder.createEnumerator(Name, Val); - subscripts.push_back(Subscript); - } + for (auto m : *te->sym->members) { + EnumMember *em = m->isEnumMember(); + llvm::StringRef Name(em->toChars()); + uint64_t Val = em->value->toInteger(); + auto Subscript = DBuilder.createEnumerator(Name, Val); + subscripts.push_back(Subscript); + } - llvm::StringRef Name = te->toChars(); - unsigned LineNumber = te->sym->loc.linnum; - ldc::DIFile File(CreateFile(te->sym->loc)); + llvm::StringRef Name = te->toChars(); + unsigned LineNumber = te->sym->loc.linnum; + ldc::DIFile File(CreateFile(te->sym->loc)); - return DBuilder.createEnumerationType( - GetCU(), - Name, - File, - LineNumber, - getTypeBitSize(T), // size (bits) - getABITypeAlign(T)*8, // align (bits) - DBuilder.getOrCreateArray(subscripts), // subscripts - CreateTypeDescription(te->sym->memtype, false) - ); + return DBuilder.createEnumerationType( + GetCU(), Name, File, LineNumber, + getTypeBitSize(T), // size (bits) + getABITypeAlign(T) * 8, // align (bits) + DBuilder.getOrCreateArray(subscripts), // subscripts + CreateTypeDescription(te->sym->memtype, false)); } -ldc::DIType ldc::DIBuilder::CreatePointerType(Type *type) -{ - llvm::Type *T = DtoType(type); - Type *t = type->toBasetype(); +ldc::DIType ldc::DIBuilder::CreatePointerType(Type *type) { + llvm::Type *T = DtoType(type); + Type *t = type->toBasetype(); - assert(t->ty == Tpointer && "Only pointers allowed for debug info in DIBuilder::CreatePointerType"); + assert( + t->ty == Tpointer && + "Only pointers allowed for debug info in DIBuilder::CreatePointerType"); - // find base type - Type *nt = t->nextOf(); - ldc::DIType basetype(CreateTypeDescription(nt, false)); + // find base type + Type *nt = t->nextOf(); + ldc::DIType basetype(CreateTypeDescription(nt, false)); - return DBuilder.createPointerType( - basetype, - getTypeBitSize(T), // size (bits) - getABITypeAlign(T)*8, // align (bits) - type->toChars() // name - ); + return DBuilder.createPointerType(basetype, + getTypeBitSize(T), // size (bits) + getABITypeAlign(T) * 8, // align (bits) + type->toChars() // name + ); } -ldc::DIType ldc::DIBuilder::CreateVectorType(Type *type) -{ - LLType* T = DtoType(type); - Type* t = type->toBasetype(); +ldc::DIType ldc::DIBuilder::CreateVectorType(Type *type) { + LLType *T = DtoType(type); + Type *t = type->toBasetype(); - assert(t->ty == Tvector && "Only vectors allowed for debug info in DIBuilder::CreateVectorType"); - TypeVector *tv = static_cast(t); - Type *te = tv->elementType(); - int64_t Dim = tv->size(Loc()) / te->size(Loc()); + assert(t->ty == Tvector && + "Only vectors allowed for debug info in DIBuilder::CreateVectorType"); + TypeVector *tv = static_cast(t); + Type *te = tv->elementType(); + int64_t Dim = tv->size(Loc()) / te->size(Loc()); #if LDC_LLVM_VER >= 306 - llvm::Metadata *subscripts[] = + llvm::Metadata *subscripts[] = #else - llvm::Value *subscripts[] = + llvm::Value *subscripts[] = #endif - { - DBuilder.getOrCreateSubrange(0, Dim) - }; - ldc::DIType basetype(CreateTypeDescription(te, false)); + {DBuilder.getOrCreateSubrange(0, Dim)}; + ldc::DIType basetype(CreateTypeDescription(te, false)); - return DBuilder.createVectorType( - getTypeBitSize(T), // size (bits) - getABITypeAlign(T)*8, // align (bits) - basetype, // element type - DBuilder.getOrCreateArray(subscripts) // subscripts - ); + return DBuilder.createVectorType( + getTypeBitSize(T), // size (bits) + getABITypeAlign(T) * 8, // align (bits) + basetype, // element type + DBuilder.getOrCreateArray(subscripts) // subscripts + ); } ldc::DIType ldc::DIBuilder::CreateMemberType(unsigned linnum, Type *type, ldc::DIFile file, - const char* c_name, - unsigned offset, - PROTKIND prot) -{ - llvm::Type *T = DtoType(type); - Type *t = type->toBasetype(); + const char *c_name, + unsigned offset, PROTKIND prot) { + llvm::Type *T = DtoType(type); + Type *t = type->toBasetype(); - // find base type - ldc::DIType basetype(CreateTypeDescription(t, true)); + // find base type + ldc::DIType basetype(CreateTypeDescription(t, true)); - unsigned Flags = 0; - switch (prot) { - case PROTprivate: - Flags = DIFlags::FlagPrivate; - break; - case PROTprotected: - Flags = DIFlags::FlagProtected; - break; + unsigned Flags = 0; + switch (prot) { + case PROTprivate: + Flags = DIFlags::FlagPrivate; + break; + case PROTprotected: + Flags = DIFlags::FlagProtected; + break; #if LDC_LLVM_VER >= 306 - case PROTpublic: - Flags = DIFlags::FlagPublic; - break; + case PROTpublic: + Flags = DIFlags::FlagPublic; + break; #endif - default: - break; - } + default: + break; + } - return DBuilder.createMemberType( - GetCU(), - c_name, // name - file, // file - linnum, // line number - getTypeBitSize(T), // size (bits) - getABITypeAlign(T)*8, // align (bits) - offset*8, // offset (bits) - Flags, // flags - basetype // derived from - ); + return DBuilder.createMemberType(GetCU(), + c_name, // name + file, // file + linnum, // line number + getTypeBitSize(T), // size (bits) + getABITypeAlign(T) * 8, // align (bits) + offset * 8, // offset (bits) + Flags, // flags + basetype // derived from + ); } void ldc::DIBuilder::AddBaseFields(ClassDeclaration *sd, ldc::DIFile file, #if LDC_LLVM_VER >= 306 - std::vector &elems + std::vector &elems #else - std::vector &elems + std::vector &elems #endif - ) -{ - if (sd->baseClass) - { - AddBaseFields(sd->baseClass, file, elems); - } + ) { + if (sd->baseClass) { + AddBaseFields(sd->baseClass, file, elems); + } - size_t narr = sd->fields.dim; - elems.reserve(narr); - for (auto vd : sd->fields) - elems.push_back(CreateMemberType(vd->loc.linnum, vd->type, file, vd->toChars(), vd->offset, vd->prot().kind)); + size_t narr = sd->fields.dim; + elems.reserve(narr); + for (auto vd : sd->fields) + elems.push_back(CreateMemberType(vd->loc.linnum, vd->type, file, + vd->toChars(), vd->offset, + vd->prot().kind)); } -ldc::DIType ldc::DIBuilder::CreateCompositeType(Type *type) -{ - Type* t = type->toBasetype(); - assert((t->ty == Tstruct || t->ty == Tclass) && - "Unsupported type for debug info in DIBuilder::CreateCompositeType"); - AggregateDeclaration* sd; - if (t->ty == Tstruct) - { - TypeStruct* ts = static_cast(t); - sd = ts->sym; - } - else - { - TypeClass* tc = static_cast(t); - sd = tc->sym; - } - assert(sd); +ldc::DIType ldc::DIBuilder::CreateCompositeType(Type *type) { + Type *t = type->toBasetype(); + assert((t->ty == Tstruct || t->ty == Tclass) && + "Unsupported type for debug info in DIBuilder::CreateCompositeType"); + AggregateDeclaration *sd; + if (t->ty == Tstruct) { + TypeStruct *ts = static_cast(t); + sd = ts->sym; + } else { + TypeClass *tc = static_cast(t); + sd = tc->sym; + } + assert(sd); - // Use the actual type associated with the declaration, ignoring any - // const/wrappers. - LLType *T = DtoType(sd->type); - IrTypeAggr *ir = sd->type->ctype->isAggr(); - assert(ir); + // Use the actual type associated with the declaration, ignoring any + // const/wrappers. + LLType *T = DtoType(sd->type); + IrTypeAggr *ir = sd->type->ctype->isAggr(); + assert(ir); - if (static_cast(ir->diCompositeType) != 0) - return ir->diCompositeType; + if (static_cast(ir->diCompositeType) != 0) + return ir->diCompositeType; - // if we don't know the aggregate's size, we don't know enough about it - // to provide debug info. probably a forward-declared struct? - if (sd->sizeok == SIZEOKnone) - return DBuilder.createUnspecifiedType(sd->toChars()); + // if we don't know the aggregate's size, we don't know enough about it + // to provide debug info. probably a forward-declared struct? + if (sd->sizeok == SIZEOKnone) + return DBuilder.createUnspecifiedType(sd->toChars()); - // elements +// elements #if LDC_LLVM_VER >= 306 - std::vector elems; + std::vector elems; #else - std::vector elems; + std::vector elems; #endif - // defaults - llvm::StringRef name = sd->toChars(); - unsigned linnum = sd->loc.linnum; - ldc::DICompileUnit CU(GetCU()); - assert(CU && "Compilation unit missing or corrupted"); - ldc::DIFile file(CreateFile(sd->loc)); + // defaults + llvm::StringRef name = sd->toChars(); + unsigned linnum = sd->loc.linnum; + ldc::DICompileUnit CU(GetCU()); + assert(CU && "Compilation unit missing or corrupted"); + ldc::DIFile file(CreateFile(sd->loc)); #if LDC_LLVM_VER >= 307 - ldc::DIType derivedFrom = nullptr; + ldc::DIType derivedFrom = nullptr; #else - ldc::DIType derivedFrom; + ldc::DIType derivedFrom; #endif - // set diCompositeType to handle recursive types properly - unsigned tag = (t->ty == Tstruct) ? llvm::dwarf::DW_TAG_structure_type - : llvm::dwarf::DW_TAG_class_type; + // set diCompositeType to handle recursive types properly + unsigned tag = (t->ty == Tstruct) ? llvm::dwarf::DW_TAG_structure_type + : llvm::dwarf::DW_TAG_class_type; #if LDC_LLVM_VER >= 307 - ir->diCompositeType = DBuilder.createReplaceableCompositeType( + ir->diCompositeType = DBuilder.createReplaceableCompositeType( #else - ir->diCompositeType = DBuilder.createReplaceableForwardDecl( + ir->diCompositeType = DBuilder.createReplaceableForwardDecl( #endif - tag, name, - CU, - file, linnum); + tag, name, CU, file, linnum); - if (!sd->isInterfaceDeclaration()) // plain interfaces don't have one - { - if (t->ty == Tstruct) - { - elems.reserve(sd->fields.dim); - for (auto vd : sd->fields) - { - ldc::DIType dt = CreateMemberType(vd->loc.linnum, vd->type, file, vd->toChars(), vd->offset, vd->prot().kind); - elems.push_back(dt); - } - } - else - { - ClassDeclaration *classDecl = sd->isClassDeclaration(); - AddBaseFields(classDecl, file, elems); - if (classDecl->baseClass) - derivedFrom = CreateCompositeType(classDecl->baseClass->getType()); - } - } - -#if LDC_LLVM_VER >= 307 - llvm::DINodeArray elemsArray = DBuilder.getOrCreateArray(elems); -#else - llvm::DIArray elemsArray = DBuilder.getOrCreateArray(elems); -#endif - - ldc::DIType ret; - if (t->ty == Tclass) { - ret = DBuilder.createClassType( - CU, // compile unit where defined - name, // name - file, // file where defined - linnum, // line number where defined - getTypeBitSize(T), // size in bits - getABITypeAlign(T)*8, // alignment in bits - 0, // offset in bits, - DIFlags::FlagFwdDecl, // flags - derivedFrom, // DerivedFrom - elemsArray - ); + if (!sd->isInterfaceDeclaration()) // plain interfaces don't have one + { + if (t->ty == Tstruct) { + elems.reserve(sd->fields.dim); + for (auto vd : sd->fields) { + ldc::DIType dt = + CreateMemberType(vd->loc.linnum, vd->type, file, vd->toChars(), + vd->offset, vd->prot().kind); + elems.push_back(dt); + } } else { - ret = DBuilder.createStructType( - CU, // compile unit where defined - name, // name - file, // file where defined - linnum, // line number where defined - getTypeBitSize(T), // size in bits - getABITypeAlign(T)*8, // alignment in bits - DIFlags::FlagFwdDecl, // flags - derivedFrom, // DerivedFrom - elemsArray - ); + ClassDeclaration *classDecl = sd->isClassDeclaration(); + AddBaseFields(classDecl, file, elems); + if (classDecl->baseClass) + derivedFrom = CreateCompositeType(classDecl->baseClass->getType()); } + } #if LDC_LLVM_VER >= 307 - ir->diCompositeType = DBuilder.replaceTemporary(llvm::TempDINode(ir->diCompositeType), static_cast(ret)); + llvm::DINodeArray elemsArray = DBuilder.getOrCreateArray(elems); #else - ir->diCompositeType.replaceAllUsesWith(ret); + llvm::DIArray elemsArray = DBuilder.getOrCreateArray(elems); #endif - ir->diCompositeType = ret; - return ret; -} + ldc::DIType ret; + if (t->ty == Tclass) { + ret = DBuilder.createClassType(CU, // compile unit where defined + name, // name + file, // file where defined + linnum, // line number where defined + getTypeBitSize(T), // size in bits + getABITypeAlign(T) * 8, // alignment in bits + 0, // offset in bits, + DIFlags::FlagFwdDecl, // flags + derivedFrom, // DerivedFrom + elemsArray); + } else { + ret = DBuilder.createStructType(CU, // compile unit where defined + name, // name + file, // file where defined + linnum, // line number where defined + getTypeBitSize(T), // size in bits + getABITypeAlign(T) * 8, // alignment in bits + DIFlags::FlagFwdDecl, // flags + derivedFrom, // DerivedFrom + elemsArray); + } -ldc::DIType ldc::DIBuilder::CreateArrayType(Type *type) -{ - llvm::Type *T = DtoType(type); - Type *t = type->toBasetype(); - - assert(t->ty == Tarray && "Only arrays allowed for debug info in DIBuilder::CreateArrayType"); - - Loc loc(IR->dmodule->srcfile->toChars(), 0, 0); - ldc::DIFile file(CreateFile(loc)); - -#if LDC_LLVM_VER >= 306 - llvm::Metadata *elems[] = -#else - llvm::Value *elems[] = -#endif - { - CreateMemberType(0, Type::tsize_t, file, "length", 0, PROTpublic), - CreateMemberType(0, t->nextOf()->pointerTo(), file, "ptr", - global.params.is64bit ? 8 : 4, PROTpublic) - }; - - return DBuilder.createStructType - ( - GetCU(), - llvm::StringRef(), // Name TODO: Really no name for arrays? t->toChars()? - file, // File - 0, // LineNo - getTypeBitSize(T), // size in bits - getABITypeAlign(T)*8, // alignment in bits - 0, // What here? #if LDC_LLVM_VER >= 307 - nullptr, // DerivedFrom + ir->diCompositeType = DBuilder.replaceTemporary( + llvm::TempDINode(ir->diCompositeType), static_cast(ret)); #else - llvm::DIType(), // DerivedFrom + ir->diCompositeType.replaceAllUsesWith(ret); #endif - DBuilder.getOrCreateArray(elems) - ); + ir->diCompositeType = ret; + + return ret; } -ldc::DIType ldc::DIBuilder::CreateSArrayType(Type *type) -{ - llvm::Type *T = DtoType(type); - Type *t = type->toBasetype(); +ldc::DIType ldc::DIBuilder::CreateArrayType(Type *type) { + llvm::Type *T = DtoType(type); + Type *t = type->toBasetype(); - // find base type -#if LDC_LLVM_VER >= 306 - llvm::SmallVector subscripts; -#else - llvm::SmallVector subscripts; -#endif - while (t->ty == Tsarray) - { - TypeSArray *tsa = static_cast(t); - int64_t Count = tsa->dim->toInteger(); -#if LDC_LLVM_VER >= 306 - llvm::Metadata *subscript = DBuilder.getOrCreateSubrange(0, Count - 1); -#else - llvm::Value *subscript = DBuilder.getOrCreateSubrange(0, Count-1); -#endif - subscripts.push_back(subscript); - t = t->nextOf(); - } - ldc::DIType basetype(CreateTypeDescription(t, false)); + assert(t->ty == Tarray && + "Only arrays allowed for debug info in DIBuilder::CreateArrayType"); - return DBuilder.createArrayType( - getTypeBitSize(T), // size (bits) - getABITypeAlign(T)*8, // align (bits) - basetype, // element type - DBuilder.getOrCreateArray(subscripts) // subscripts - ); + Loc loc(IR->dmodule->srcfile->toChars(), 0, 0); + ldc::DIFile file(CreateFile(loc)); + +#if LDC_LLVM_VER >= 306 + llvm::Metadata *elems[] = +#else + llvm::Value *elems[] = +#endif + {CreateMemberType(0, Type::tsize_t, file, "length", 0, PROTpublic), + CreateMemberType(0, t->nextOf()->pointerTo(), file, "ptr", + global.params.is64bit ? 8 : 4, PROTpublic)}; + + return DBuilder.createStructType( + GetCU(), + llvm::StringRef(), // Name TODO: Really no name for arrays? t->toChars()? + file, // File + 0, // LineNo + getTypeBitSize(T), // size in bits + getABITypeAlign(T) * 8, // alignment in bits + 0, // What here? +#if LDC_LLVM_VER >= 307 + nullptr, // DerivedFrom +#else + llvm::DIType(), // DerivedFrom +#endif + DBuilder.getOrCreateArray(elems)); } -ldc::DIType ldc::DIBuilder::CreateAArrayType(Type *type) -{ - // FIXME: Implement - return DBuilder.createUnspecifiedType(type->toChars()); +ldc::DIType ldc::DIBuilder::CreateSArrayType(Type *type) { + llvm::Type *T = DtoType(type); + Type *t = type->toBasetype(); + +// find base type +#if LDC_LLVM_VER >= 306 + llvm::SmallVector subscripts; +#else + llvm::SmallVector subscripts; +#endif + while (t->ty == Tsarray) { + TypeSArray *tsa = static_cast(t); + int64_t Count = tsa->dim->toInteger(); +#if LDC_LLVM_VER >= 306 + llvm::Metadata *subscript = DBuilder.getOrCreateSubrange(0, Count - 1); +#else + llvm::Value *subscript = DBuilder.getOrCreateSubrange(0, Count - 1); +#endif + subscripts.push_back(subscript); + t = t->nextOf(); + } + ldc::DIType basetype(CreateTypeDescription(t, false)); + + return DBuilder.createArrayType( + getTypeBitSize(T), // size (bits) + getABITypeAlign(T) * 8, // align (bits) + basetype, // element type + DBuilder.getOrCreateArray(subscripts) // subscripts + ); +} + +ldc::DIType ldc::DIBuilder::CreateAArrayType(Type *type) { + // FIXME: Implement + return DBuilder.createUnspecifiedType(type->toChars()); } //////////////////////////////////////////////////////////////////////////////// -ldc::DISubroutineType ldc::DIBuilder::CreateFunctionType(Type *type) -{ - TypeFunction *t = static_cast(type); - Type *retType = t->next; +ldc::DISubroutineType ldc::DIBuilder::CreateFunctionType(Type *type) { + TypeFunction *t = static_cast(type); + Type *retType = t->next; - Loc loc(IR->dmodule->srcfile->toChars(), 0, 0); - ldc::DIFile file(CreateFile(loc)); + Loc loc(IR->dmodule->srcfile->toChars(), 0, 0); + ldc::DIFile file(CreateFile(loc)); - // Create "dummy" subroutine type for the return type +// Create "dummy" subroutine type for the return type #if LDC_LLVM_VER >= 306 - llvm::SmallVector Elts; + llvm::SmallVector Elts; #else - llvm::SmallVector Elts; + llvm::SmallVector Elts; #endif - Elts.push_back(CreateTypeDescription(retType, true)); + Elts.push_back(CreateTypeDescription(retType, true)); #if LDC_LLVM_VER >= 307 - llvm::DITypeRefArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); + llvm::DITypeRefArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); #elif LDC_LLVM_VER >= 306 - llvm::DITypeArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); + llvm::DITypeArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); #else - llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(Elts); + llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(Elts); #endif #if LDC_LLVM_VER >= 308 - return DBuilder.createSubroutineType(EltTypeArray); + return DBuilder.createSubroutineType(EltTypeArray); #else return DBuilder.createSubroutineType(file, EltTypeArray); #endif } -ldc::DISubroutineType ldc::DIBuilder::CreateDelegateType(Type *type) -{ - // FIXME: Implement - TypeDelegate *t = static_cast(type); +ldc::DISubroutineType ldc::DIBuilder::CreateDelegateType(Type *type) { + // FIXME: Implement + TypeDelegate *t = static_cast(type); - Loc loc(IR->dmodule->srcfile->toChars(), 0, 0); - ldc::DIFile file(CreateFile(loc)); + Loc loc(IR->dmodule->srcfile->toChars(), 0, 0); + ldc::DIFile file(CreateFile(loc)); - // Create "dummy" subroutine type for the return type +// Create "dummy" subroutine type for the return type #if LDC_LLVM_VER >= 306 - llvm::SmallVector Elts; + llvm::SmallVector Elts; #else - llvm::SmallVector Elts; + llvm::SmallVector Elts; #endif - Elts.push_back( - DBuilder.createUnspecifiedType(type->toChars()) - ); + Elts.push_back(DBuilder.createUnspecifiedType(type->toChars())); #if LDC_LLVM_VER >= 307 - llvm::DITypeRefArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); + llvm::DITypeRefArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); #elif LDC_LLVM_VER >= 306 - llvm::DITypeArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); + llvm::DITypeArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); #else - llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(Elts); + llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(Elts); #endif #if LDC_LLVM_VER >= 308 - return DBuilder.createSubroutineType(EltTypeArray); + return DBuilder.createSubroutineType(EltTypeArray); #else - return DBuilder.createSubroutineType(file, EltTypeArray); + return DBuilder.createSubroutineType(file, EltTypeArray); #endif } //////////////////////////////////////////////////////////////////////////////// -ldc::DIType ldc::DIBuilder::CreateTypeDescription(Type* type, - bool derefclass) -{ - Type *t = type->toBasetype(); - if (derefclass && t->ty == Tclass) - { - type = type->pointerTo(); - t = type->toBasetype(); - } +ldc::DIType ldc::DIBuilder::CreateTypeDescription(Type *type, bool derefclass) { + Type *t = type->toBasetype(); + if (derefclass && t->ty == Tclass) { + type = type->pointerTo(); + t = type->toBasetype(); + } - if (t->ty == Tvoid || t->ty == Tnull) - return DBuilder.createUnspecifiedType(t->toChars()); - else if (t->isintegral() || t->isfloating()) - { - if (t->ty == Tvector) - return CreateVectorType(type); - if (type->ty == Tenum) - return CreateEnumType(type); - return CreateBasicType(type); - } - else if (t->ty == Tpointer) - return CreatePointerType(type); - else if (t->ty == Tarray) - return CreateArrayType(type); - else if (t->ty == Tsarray) - return CreateSArrayType(type); - else if (t->ty == Taarray) - return CreateAArrayType(type); - else if (t->ty == Tstruct || t->ty == Tclass) - return CreateCompositeType(type); - else if (t->ty == Tfunction) - return CreateFunctionType(type); - else if (t->ty == Tdelegate) - return CreateDelegateType(type); + if (t->ty == Tvoid || t->ty == Tnull) + return DBuilder.createUnspecifiedType(t->toChars()); + else if (t->isintegral() || t->isfloating()) { + if (t->ty == Tvector) + return CreateVectorType(type); + if (type->ty == Tenum) + return CreateEnumType(type); + return CreateBasicType(type); + } else if (t->ty == Tpointer) + return CreatePointerType(type); + else if (t->ty == Tarray) + return CreateArrayType(type); + else if (t->ty == Tsarray) + return CreateSArrayType(type); + else if (t->ty == Taarray) + return CreateAArrayType(type); + else if (t->ty == Tstruct || t->ty == Tclass) + return CreateCompositeType(type); + else if (t->ty == Tfunction) + return CreateFunctionType(type); + else if (t->ty == Tdelegate) + return CreateDelegateType(type); - // Crash if the type is not supported. - llvm_unreachable("Unsupported type in debug info"); + // Crash if the type is not supported. + llvm_unreachable("Unsupported type in debug info"); } //////////////////////////////////////////////////////////////////////////////// -void ldc::DIBuilder::EmitCompileUnit(Module *m) -{ - if (!global.params.symdebug) - return; +void ldc::DIBuilder::EmitCompileUnit(Module *m) { + if (!global.params.symdebug) + return; - Logger::println("D to dwarf compile_unit"); - LOG_SCOPE; + Logger::println("D to dwarf compile_unit"); + LOG_SCOPE; - assert(!CUNode && - "Already created compile unit for this DIBuilder instance"); + assert(!CUNode && "Already created compile unit for this DIBuilder instance"); - // prepare srcpath - llvm::SmallString<128> srcpath(m->srcfile->name->toChars()); - llvm::sys::fs::make_absolute(srcpath); + // prepare srcpath + llvm::SmallString<128> srcpath(m->srcfile->name->toChars()); + llvm::sys::fs::make_absolute(srcpath); #if LDC_LLVM_VER >= 308 - if (global.params.targetTriple.isWindowsMSVCEnvironment()) - IR->module.addModuleFlag(llvm::Module::Warning, "CodeView", 1); + if (global.params.targetTriple.isWindowsMSVCEnvironment()) + IR->module.addModuleFlag(llvm::Module::Warning, "CodeView", 1); #endif - // Metadata without a correct version will be stripped by UpgradeDebugInfo. - IR->module.addModuleFlag(llvm::Module::Warning, "Debug Info Version", llvm::DEBUG_METADATA_VERSION); + // Metadata without a correct version will be stripped by UpgradeDebugInfo. + IR->module.addModuleFlag(llvm::Module::Warning, "Debug Info Version", + llvm::DEBUG_METADATA_VERSION); - CUNode = - DBuilder.createCompileUnit( - global.params.symdebug == 2 ? llvm::dwarf::DW_LANG_C - : llvm::dwarf::DW_LANG_D, - llvm::sys::path::filename(srcpath), - llvm::sys::path::parent_path(srcpath), - "LDC (http://wiki.dlang.org/LDC)", - isOptimizationEnabled(), // isOptimized - llvm::StringRef(), // Flags TODO - 1 // Runtime Version TODO - ); + CUNode = DBuilder.createCompileUnit( + global.params.symdebug == 2 ? llvm::dwarf::DW_LANG_C + : llvm::dwarf::DW_LANG_D, + llvm::sys::path::filename(srcpath), llvm::sys::path::parent_path(srcpath), + "LDC (http://wiki.dlang.org/LDC)", + isOptimizationEnabled(), // isOptimized + llvm::StringRef(), // Flags TODO + 1 // Runtime Version TODO + ); } -ldc::DISubprogram ldc::DIBuilder::EmitSubProgram(FuncDeclaration *fd) -{ - if (!global.params.symdebug) +ldc::DISubprogram ldc::DIBuilder::EmitSubProgram(FuncDeclaration *fd) { + if (!global.params.symdebug) #if LDC_LLVM_VER >= 307 - return nullptr; + return nullptr; #else - return llvm::DISubprogram(); + return llvm::DISubprogram(); #endif - Logger::println("D to dwarf subprogram"); - LOG_SCOPE; + Logger::println("D to dwarf subprogram"); + LOG_SCOPE; - ldc::DICompileUnit CU(GetCU()); - assert(CU && "Compilation unit missing or corrupted in DIBuilder::EmitSubProgram"); + ldc::DICompileUnit CU(GetCU()); + assert(CU && + "Compilation unit missing or corrupted in DIBuilder::EmitSubProgram"); - ldc::DIFile file(CreateFile(fd->loc)); + ldc::DIFile file(CreateFile(fd->loc)); - // Create subroutine type - ldc::DISubroutineType DIFnType = CreateFunctionType(static_cast(fd->type)); + // Create subroutine type + ldc::DISubroutineType DIFnType = + CreateFunctionType(static_cast(fd->type)); - // FIXME: duplicates ? - return DBuilder.createFunction( - CU, // context - fd->toPrettyChars(), // name - mangleExact(fd), // linkage name - file, // file - fd->loc.linnum, // line no - DIFnType, // type - fd->protection == PROTprivate, // is local to unit - true, // isdefinition - fd->loc.linnum, // FIXME: scope line - DIFlags::FlagPrototyped, // Flags - isOptimizationEnabled(), // isOptimized - getIrFunc(fd)->func - ); + // FIXME: duplicates ? + return DBuilder.createFunction(CU, // context + fd->toPrettyChars(), // name + mangleExact(fd), // linkage name + file, // file + fd->loc.linnum, // line no + DIFnType, // type + fd->protection == + PROTprivate, // is local to unit + true, // isdefinition + fd->loc.linnum, // FIXME: scope line + DIFlags::FlagPrototyped, // Flags + isOptimizationEnabled(), // isOptimized + getIrFunc(fd)->func); } -ldc::DISubprogram ldc::DIBuilder::EmitModuleCTor(llvm::Function* Fn, - llvm::StringRef prettyname) -{ - if (!global.params.symdebug) +ldc::DISubprogram ldc::DIBuilder::EmitModuleCTor(llvm::Function *Fn, + llvm::StringRef prettyname) { + if (!global.params.symdebug) #if LDC_LLVM_VER >= 307 - return nullptr; + return nullptr; #else - return llvm::DISubprogram(); + return llvm::DISubprogram(); #endif - Logger::println("D to dwarf subprogram"); - LOG_SCOPE; + Logger::println("D to dwarf subprogram"); + LOG_SCOPE; - ldc::DICompileUnit CU(GetCU()); - assert(CU && "Compilation unit missing or corrupted in DIBuilder::EmitSubProgram"); + ldc::DICompileUnit CU(GetCU()); + assert(CU && + "Compilation unit missing or corrupted in DIBuilder::EmitSubProgram"); - Loc loc(IR->dmodule->srcfile->toChars(), 0, 0); - ldc::DIFile file(CreateFile(loc)); + Loc loc(IR->dmodule->srcfile->toChars(), 0, 0); + ldc::DIFile file(CreateFile(loc)); - // Create "dummy" subroutine type for the return type +// Create "dummy" subroutine type for the return type #if LDC_LLVM_VER >= 306 - llvm::SmallVector Elts; + llvm::SmallVector Elts; #else - llvm::SmallVector Elts; + llvm::SmallVector Elts; #endif - Elts.push_back(CreateTypeDescription(Type::tvoid, true)); + Elts.push_back(CreateTypeDescription(Type::tvoid, true)); #if LDC_LLVM_VER >= 307 - llvm::DITypeRefArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); + llvm::DITypeRefArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); #elif LDC_LLVM_VER >= 306 - llvm::DITypeArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); + llvm::DITypeArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts); #else - llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(Elts); + llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(Elts); #endif #if LDC_LLVM_VER >= 308 - ldc::DISubroutineType DIFnType = DBuilder.createSubroutineType(EltTypeArray); + ldc::DISubroutineType DIFnType = DBuilder.createSubroutineType(EltTypeArray); #else - ldc::DISubroutineType DIFnType = DBuilder.createSubroutineType(file, EltTypeArray); + ldc::DISubroutineType DIFnType = + DBuilder.createSubroutineType(file, EltTypeArray); #endif - // FIXME: duplicates ? - return DBuilder.createFunction( - CU, // context - prettyname, // name - Fn->getName(), // linkage name - file, // file - 0, // line no - DIFnType, // return type. TODO: fill it up - true, // is local to unit - true, // isdefinition - 0, // FIXME: scope line - DIFlags::FlagPrototyped | DIFlags::FlagArtificial, - isOptimizationEnabled(), // isOptimized - Fn - ); + // FIXME: duplicates ? + return DBuilder.createFunction(CU, // context + prettyname, // name + Fn->getName(), // linkage name + file, // file + 0, // line no + DIFnType, // return type. TODO: fill it up + true, // is local to unit + true, // isdefinition + 0, // FIXME: scope line + DIFlags::FlagPrototyped | + DIFlags::FlagArtificial, + isOptimizationEnabled(), // isOptimized + Fn); } -void ldc::DIBuilder::EmitFuncStart(FuncDeclaration *fd) -{ - if (!global.params.symdebug) - return; +void ldc::DIBuilder::EmitFuncStart(FuncDeclaration *fd) { + if (!global.params.symdebug) + return; - Logger::println("D to dwarf funcstart"); - LOG_SCOPE; + Logger::println("D to dwarf funcstart"); + LOG_SCOPE; - assert(static_cast(getIrFunc(fd)->diSubprogram) != 0); - EmitStopPoint(fd->loc); + assert(static_cast(getIrFunc(fd)->diSubprogram) != 0); + EmitStopPoint(fd->loc); } -void ldc::DIBuilder::EmitFuncEnd(FuncDeclaration *fd) -{ - if (!global.params.symdebug) - return; +void ldc::DIBuilder::EmitFuncEnd(FuncDeclaration *fd) { + if (!global.params.symdebug) + return; - Logger::println("D to dwarf funcend"); - LOG_SCOPE; + Logger::println("D to dwarf funcend"); + LOG_SCOPE; - assert(static_cast(getIrFunc(fd)->diSubprogram) != 0); - EmitStopPoint(fd->endloc); + assert(static_cast(getIrFunc(fd)->diSubprogram) != 0); + EmitStopPoint(fd->endloc); } -void ldc::DIBuilder::EmitBlockStart(Loc& loc) -{ - if (!global.params.symdebug) - return; +void ldc::DIBuilder::EmitBlockStart(Loc &loc) { + if (!global.params.symdebug) + return; - Logger::println("D to dwarf block start"); - LOG_SCOPE; + Logger::println("D to dwarf block start"); + LOG_SCOPE; - ldc::DILexicalBlock block = DBuilder.createLexicalBlock( - GetCurrentScope(), // scope - CreateFile(loc), // file - loc.linnum, // line - loc.linnum ? loc.charnum : 0 // column + ldc::DILexicalBlock block = + DBuilder.createLexicalBlock(GetCurrentScope(), // scope + CreateFile(loc), // file + loc.linnum, // line + loc.linnum ? loc.charnum : 0 // column #if LDC_LLVM_VER == 305 - , 0 // DWARF path discriminator value + , + 0 // DWARF path discriminator value #endif - ); - IR->func()->diLexicalBlocks.push(block); - EmitStopPoint(loc); + ); + IR->func()->diLexicalBlocks.push(block); + EmitStopPoint(loc); } -void ldc::DIBuilder::EmitBlockEnd() -{ - if (!global.params.symdebug) - return; +void ldc::DIBuilder::EmitBlockEnd() { + if (!global.params.symdebug) + return; - Logger::println("D to dwarf block end"); - LOG_SCOPE; + Logger::println("D to dwarf block end"); + LOG_SCOPE; - IrFunction *fn = IR->func(); - assert(!fn->diLexicalBlocks.empty()); - fn->diLexicalBlocks.pop(); + IrFunction *fn = IR->func(); + assert(!fn->diLexicalBlocks.empty()); + fn->diLexicalBlocks.pop(); } -void ldc::DIBuilder::EmitStopPoint(Loc& loc) -{ - if (!global.params.symdebug) - return; +void ldc::DIBuilder::EmitStopPoint(Loc &loc) { + if (!global.params.symdebug) + return; - // If we already have a location set and the current loc is invalid - // (line 0), then we can just ignore it (see GitHub issue #998 for why we - // cannot do this in all cases). - if (!loc.linnum && + // If we already have a location set and the current loc is invalid + // (line 0), then we can just ignore it (see GitHub issue #998 for why we + // cannot do this in all cases). + if (!loc.linnum && #if LDC_LLVM_VER >= 307 - IR->ir->getCurrentDebugLocation() + IR->ir->getCurrentDebugLocation() #else - !IR->ir->getCurrentDebugLocation().isUnknown() + !IR->ir->getCurrentDebugLocation().isUnknown() #endif - ) - return; + ) + return; - unsigned charnum = (loc.linnum ? loc.charnum : 0); - Logger::println("D to dwarf stoppoint at line %u, column %u", loc.linnum, charnum); - LOG_SCOPE; - IR->ir->SetCurrentDebugLocation(llvm::DebugLoc::get(loc.linnum, charnum, GetCurrentScope())); + unsigned charnum = (loc.linnum ? loc.charnum : 0); + Logger::println("D to dwarf stoppoint at line %u, column %u", loc.linnum, + charnum); + LOG_SCOPE; + IR->ir->SetCurrentDebugLocation( + llvm::DebugLoc::get(loc.linnum, charnum, GetCurrentScope())); } -void ldc::DIBuilder::EmitValue(llvm::Value *val, VarDeclaration *vd) -{ - auto sub = IR->func()->variableMap.find(vd); - if (sub == IR->func()->variableMap.end()) - return; +void ldc::DIBuilder::EmitValue(llvm::Value *val, VarDeclaration *vd) { + auto sub = IR->func()->variableMap.find(vd); + if (sub == IR->func()->variableMap.end()) + return; - ldc::DILocalVariable debugVariable = sub->second; - if (!global.params.symdebug || !debugVariable) - return; + ldc::DILocalVariable debugVariable = sub->second; + if (!global.params.symdebug || !debugVariable) + return; - llvm::Instruction *instr = DBuilder.insertDbgValueIntrinsic(val, 0, debugVariable, + llvm::Instruction *instr = + DBuilder.insertDbgValueIntrinsic(val, 0, debugVariable, #if LDC_LLVM_VER >= 306 - DBuilder.createExpression(), + DBuilder.createExpression(), #endif #if LDC_LLVM_VER >= 307 - IR->ir->getCurrentDebugLocation(), + IR->ir->getCurrentDebugLocation(), #endif - IR->scopebb()); - instr->setDebugLoc(IR->ir->getCurrentDebugLocation()); + IR->scopebb()); + instr->setDebugLoc(IR->ir->getCurrentDebugLocation()); } void ldc::DIBuilder::EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd, @@ -857,154 +802,148 @@ void ldc::DIBuilder::EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd, #else llvm::ArrayRef addr #endif - ) -{ - if (!global.params.symdebug) - return; + ) { + if (!global.params.symdebug) + return; - Logger::println("D to dwarf local variable"); - LOG_SCOPE; + Logger::println("D to dwarf local variable"); + LOG_SCOPE; - auto& variableMap = IR->func()->variableMap; - auto sub = variableMap.find(vd); - if (sub != variableMap.end()) - return; // ensure that the debug variable is created only once + auto &variableMap = IR->func()->variableMap; + auto sub = variableMap.find(vd); + if (sub != variableMap.end()) + return; // ensure that the debug variable is created only once - // get type description - ldc::DIType TD = CreateTypeDescription(type ? type : vd->type, true); - if (static_cast(TD) == 0) - return; // unsupported + // get type description + ldc::DIType TD = CreateTypeDescription(type ? type : vd->type, true); + if (static_cast(TD) == 0) + return; // unsupported - if (vd->storage_class & (STCref | STCout)) - TD = DBuilder.createReferenceType(llvm::dwarf::DW_TAG_reference_type, TD); + if (vd->storage_class & (STCref | STCout)) + TD = DBuilder.createReferenceType(llvm::dwarf::DW_TAG_reference_type, TD); - // get variable description - assert(!vd->isDataseg() && "static variable"); + // get variable description + assert(!vd->isDataseg() && "static variable"); #if LDC_LLVM_VER < 308 - unsigned tag; - if (vd->isParameter()) - tag = llvm::dwarf::DW_TAG_arg_variable; - else - tag = llvm::dwarf::DW_TAG_auto_variable; + unsigned tag; + if (vd->isParameter()) + tag = llvm::dwarf::DW_TAG_arg_variable; + else + tag = llvm::dwarf::DW_TAG_auto_variable; #endif - ldc::DILocalVariable debugVariable; - unsigned Flags = 0; - if (isThisPtr) - Flags |= DIFlags::FlagArtificial | DIFlags::FlagObjectPointer; + ldc::DILocalVariable debugVariable; + unsigned Flags = 0; + if (isThisPtr) + Flags |= DIFlags::FlagArtificial | DIFlags::FlagObjectPointer; #if LDC_LLVM_VER < 306 - if (addr.empty()) { + if (addr.empty()) { #endif #if LDC_LLVM_VER >= 308 - if (vd->isParameter()) - { - FuncDeclaration* fd = vd->parent->isFuncDeclaration(); - assert(fd); - int argNo; - if (fd->vthis == vd) - argNo = 0; - else - { - assert(fd->parameters); - for (argNo = 0; argNo < fd->parameters->dim; argNo++) - if ((*fd->parameters)[argNo] == vd) - break; - assert(argNo < fd->parameters->dim); - if (fd->vthis) - argNo++; - } - - debugVariable = DBuilder.createParameterVariable( - GetCurrentScope(), // scope - vd->toChars(), // name - argNo + 1, - CreateFile(vd->loc), // file - vd->loc.linnum, // line num - TD, // type - true, // preserve - Flags // flags - ); + if (vd->isParameter()) { + FuncDeclaration *fd = vd->parent->isFuncDeclaration(); + assert(fd); + int argNo; + if (fd->vthis == vd) + argNo = 0; + else { + assert(fd->parameters); + for (argNo = 0; argNo < fd->parameters->dim; argNo++) + if ((*fd->parameters)[argNo] == vd) + break; + assert(argNo < fd->parameters->dim); + if (fd->vthis) + argNo++; } - else - debugVariable = DBuilder.createAutoVariable( - GetCurrentScope(), // scope - vd->toChars(), // name - CreateFile(vd->loc), // file - vd->loc.linnum, // line num - TD, // type - true, // preserve - Flags // flags - ); + + debugVariable = + DBuilder.createParameterVariable(GetCurrentScope(), // scope + vd->toChars(), // name + argNo + 1, + CreateFile(vd->loc), // file + vd->loc.linnum, // line num + TD, // type + true, // preserve + Flags // flags + ); + } else + debugVariable = DBuilder.createAutoVariable(GetCurrentScope(), // scope + vd->toChars(), // name + CreateFile(vd->loc), // file + vd->loc.linnum, // line num + TD, // type + true, // preserve + Flags // flags + ); #else - debugVariable = DBuilder.createLocalVariable( - tag, // tag - GetCurrentScope(), // scope - vd->toChars(), // name - CreateFile(vd->loc), // file - vd->loc.linnum, // line num - TD, // type - true, // preserve - Flags // flags - ); + debugVariable = DBuilder.createLocalVariable(tag, // tag + GetCurrentScope(), // scope + vd->toChars(), // name + CreateFile(vd->loc), // file + vd->loc.linnum, // line num + TD, // type + true, // preserve + Flags // flags + ); #endif #if LDC_LLVM_VER < 306 - } - else { - debugVariable = DBuilder.createComplexVariable( - tag, // tag - GetCurrentScope(), // scope - vd->toChars(), // name - CreateFile(vd->loc), // file - vd->loc.linnum, // line num - TD, // type - addr - ); - } + } else { + debugVariable = DBuilder.createComplexVariable(tag, // tag + GetCurrentScope(), // scope + vd->toChars(), // name + CreateFile(vd->loc), // file + vd->loc.linnum, // line num + TD, // type + addr); + } #endif - variableMap[vd] = debugVariable; + variableMap[vd] = debugVariable; - // declare +// declare #if LDC_LLVM_VER >= 306 - Declare(vd->loc, ll, debugVariable, addr.empty() ? DBuilder.createExpression() : DBuilder.createExpression(addr)); + Declare(vd->loc, ll, debugVariable, addr.empty() + ? DBuilder.createExpression() + : DBuilder.createExpression(addr)); #else - Declare(vd->loc, ll, debugVariable); + Declare(vd->loc, ll, debugVariable); #endif } -ldc::DIGlobalVariable ldc::DIBuilder::EmitGlobalVariable(llvm::GlobalVariable *ll, VarDeclaration *vd) -{ - if (!global.params.symdebug) +ldc::DIGlobalVariable +ldc::DIBuilder::EmitGlobalVariable(llvm::GlobalVariable *ll, + VarDeclaration *vd) { + if (!global.params.symdebug) #if LDC_LLVM_VER >= 307 - return nullptr; + return nullptr; #else - return llvm::DIGlobalVariable(); + return llvm::DIGlobalVariable(); #endif - Logger::println("D to dwarf global_variable"); - LOG_SCOPE; + Logger::println("D to dwarf global_variable"); + LOG_SCOPE; - assert(vd->isDataseg() || (vd->storage_class & (STCconst | STCimmutable) && vd->init)); + assert(vd->isDataseg() || + (vd->storage_class & (STCconst | STCimmutable) && vd->init)); - return DBuilder.createGlobalVariable( + return DBuilder.createGlobalVariable( #if LDC_LLVM_VER >= 306 - GetCU(), // context + GetCU(), // context #endif - vd->toChars(), // name - mangle(vd), // linkage name - CreateFile(vd->loc), // file - vd->loc.linnum, // line num - CreateTypeDescription(vd->type, false), // type - vd->protection == PROTprivate, // is local to unit - ll // value - ); + vd->toChars(), // name + mangle(vd), // linkage name + CreateFile(vd->loc), // file + vd->loc.linnum, // line num + CreateTypeDescription(vd->type, false), // type + vd->protection == PROTprivate, // is local to unit + ll // value + ); } -void ldc::DIBuilder::Finalize() -{ - if (!global.params.symdebug) - return; +void ldc::DIBuilder::Finalize() { + if (!global.params.symdebug) + return; - DBuilder.finalize(); + DBuilder.finalize(); } diff --git a/gen/dibuilder.h b/gen/dibuilder.h index 117a9c184c..18471351cb 100644 --- a/gen/dibuilder.h +++ b/gen/dibuilder.h @@ -29,31 +29,31 @@ class Type; class VarDeclaration; namespace llvm { - class GlobalVariable; - class StructType; - class LLVMContext; +class GlobalVariable; +class StructType; +class LLVMContext; // Only for the OpXXX templates, see below. - class DataLayout; +class DataLayout; } // Only for the OpXXX templates, see below. -extern const llvm::DataLayout* gDataLayout; +extern const llvm::DataLayout *gDataLayout; namespace ldc { // Define some basic types #if LDC_LLVM_VER >= 307 -typedef llvm::DIType* DIType; -typedef llvm::DIFile* DIFile; -typedef llvm::DIGlobalVariable* DIGlobalVariable; -typedef llvm::DILocalVariable* DILocalVariable; -typedef llvm::DIExpression* DIExpression; -typedef llvm::DILexicalBlock* DILexicalBlock; -typedef llvm::DIScope* DIScope; -typedef llvm::DISubroutineType* DISubroutineType; -typedef llvm::DISubprogram* DISubprogram; -typedef llvm::DICompileUnit* DICompileUnit; +typedef llvm::DIType *DIType; +typedef llvm::DIFile *DIFile; +typedef llvm::DIGlobalVariable *DIGlobalVariable; +typedef llvm::DILocalVariable *DILocalVariable; +typedef llvm::DIExpression *DIExpression; +typedef llvm::DILexicalBlock *DILexicalBlock; +typedef llvm::DIScope *DIScope; +typedef llvm::DISubroutineType *DISubroutineType; +typedef llvm::DISubprogram *DISubprogram; +typedef llvm::DICompileUnit *DICompileUnit; #else typedef llvm::DIType DIType; typedef llvm::DIFile DIFile; @@ -69,160 +69,160 @@ typedef llvm::DIExpression DIExpression; #endif #endif -class DIBuilder -{ - IRState *const IR; - llvm::DIBuilder DBuilder; +class DIBuilder { + IRState *const IR; + llvm::DIBuilder DBuilder; #if LDC_LLVM_VER >= 307 - DICompileUnit CUNode; + DICompileUnit CUNode; #else - const llvm::MDNode *CUNode; + const llvm::MDNode *CUNode; #endif - DICompileUnit GetCU() - { + DICompileUnit GetCU() { #if LDC_LLVM_VER >= 307 - return CUNode; + return CUNode; #else - return llvm::DICompileUnit(CUNode); + return llvm::DICompileUnit(CUNode); #endif - } + } public: - DIBuilder(IRState *const IR); + DIBuilder(IRState *const IR); - /// \brief Emit the Dwarf compile_unit global for a Module m. - /// \param m Module to emit as compile unit. - void EmitCompileUnit(Module *m); + /// \brief Emit the Dwarf compile_unit global for a Module m. + /// \param m Module to emit as compile unit. + void EmitCompileUnit(Module *m); - /// \brief Emit the Dwarf subprogram global for a function declaration fd. - /// \param fd Function declaration to emit as subprogram. - /// \returns the Dwarf subprogram global. - DISubprogram EmitSubProgram(FuncDeclaration *fd); // FIXME + /// \brief Emit the Dwarf subprogram global for a function declaration fd. + /// \param fd Function declaration to emit as subprogram. + /// \returns the Dwarf subprogram global. + DISubprogram EmitSubProgram(FuncDeclaration *fd); // FIXME - /// \brief Emit the Dwarf subprogram global for a module ctor. - /// This is used for generated functions like moduleinfoctors, - /// module ctors/dtors and unittests. - /// \param Fn llvm::Function pointer. - /// \param prettyname The name as seen in the source. - /// \returns the Dwarf subprogram global. - DISubprogram EmitModuleCTor(llvm::Function* Fn, llvm::StringRef prettyname); // FIXME + /// \brief Emit the Dwarf subprogram global for a module ctor. + /// This is used for generated functions like moduleinfoctors, + /// module ctors/dtors and unittests. + /// \param Fn llvm::Function pointer. + /// \param prettyname The name as seen in the source. + /// \returns the Dwarf subprogram global. + DISubprogram EmitModuleCTor(llvm::Function *Fn, + llvm::StringRef prettyname); // FIXME - /// \brief Emits debug info for function start - void EmitFuncStart(FuncDeclaration *fd); + /// \brief Emits debug info for function start + void EmitFuncStart(FuncDeclaration *fd); - /// \brief Emits debug info for function end - void EmitFuncEnd(FuncDeclaration *fd); + /// \brief Emits debug info for function end + void EmitFuncEnd(FuncDeclaration *fd); - /// \brief Emits debug info for block start - void EmitBlockStart(Loc& loc); + /// \brief Emits debug info for block start + void EmitBlockStart(Loc &loc); - /// \brief Emits debug info for block end - void EmitBlockEnd(); + /// \brief Emits debug info for block end + void EmitBlockEnd(); - void EmitStopPoint(Loc& loc); + void EmitStopPoint(Loc &loc); - void EmitValue(llvm::Value *val, VarDeclaration* vd); + void EmitValue(llvm::Value *val, VarDeclaration *vd); - /// \brief Emits all things necessary for making debug info for a local variable vd. - /// \param ll LLVM Value of the variable. - /// \param vd Variable declaration to emit debug info for. - /// \param type Type of parameter if diferent from vd->type - /// \param isThisPtr Parameter is hidden this pointer - /// \param addr An array of complex address operations. - void EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd, - Type *type = 0, - bool isThisPtr = false, + /// \brief Emits all things necessary for making debug info for a local + /// variable vd. + /// \param ll LLVM Value of the variable. + /// \param vd Variable declaration to emit debug info for. + /// \param type Type of parameter if diferent from vd->type + /// \param isThisPtr Parameter is hidden this pointer + /// \param addr An array of complex address operations. + void + EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd, Type *type = 0, + bool isThisPtr = false, #if LDC_LLVM_VER >= 306 - llvm::ArrayRef addr = llvm::ArrayRef() + llvm::ArrayRef addr = llvm::ArrayRef() #else - llvm::ArrayRef addr = llvm::ArrayRef() + llvm::ArrayRef addr = + llvm::ArrayRef() #endif - ); + ); - /// \brief Emits all things necessary for making debug info for a global variable vd. - /// \param ll LLVM global variable - /// \param vd Variable declaration to emit debug info for. - DIGlobalVariable EmitGlobalVariable(llvm::GlobalVariable *ll, VarDeclaration *vd); // FIXME + /// \brief Emits all things necessary for making debug info for a global + /// variable vd. + /// \param ll LLVM global variable + /// \param vd Variable declaration to emit debug info for. + DIGlobalVariable EmitGlobalVariable(llvm::GlobalVariable *ll, + VarDeclaration *vd); // FIXME - void Finalize(); + void Finalize(); private: - llvm::LLVMContext &getContext(); - Module *getDefinedModule(Dsymbol *s); - DIScope GetCurrentScope(); - void Declare(const Loc &loc, llvm::Value *var, ldc::DILocalVariable divar + llvm::LLVMContext &getContext(); + Module *getDefinedModule(Dsymbol *s); + DIScope GetCurrentScope(); + void Declare(const Loc &loc, llvm::Value *var, ldc::DILocalVariable divar #if LDC_LLVM_VER >= 306 - , ldc::DIExpression diexpr + , + ldc::DIExpression diexpr #endif - ); - void AddBaseFields(ClassDeclaration *sd, ldc::DIFile file, + ); + void AddBaseFields(ClassDeclaration *sd, ldc::DIFile file, #if LDC_LLVM_VER >= 306 - std::vector &elems + std::vector &elems #else - std::vector &elems + std::vector &elems #endif - ); - DIFile CreateFile(Loc& loc); - DIType CreateBasicType(Type *type); - DIType CreateEnumType(Type *type); - DIType CreatePointerType(Type *type); - DIType CreateVectorType(Type *type); - DIType CreateMemberType(unsigned linnum, Type *type, DIFile file, const char* c_name, unsigned offset, PROTKIND); - DIType CreateCompositeType(Type *type); - DIType CreateArrayType(Type *type); - DIType CreateSArrayType(Type *type); - DIType CreateAArrayType(Type *type); - DISubroutineType CreateFunctionType(Type *type); - DISubroutineType CreateDelegateType(Type *type); - DIType CreateTypeDescription(Type* type, bool derefclass = false); + ); + DIFile CreateFile(Loc &loc); + DIType CreateBasicType(Type *type); + DIType CreateEnumType(Type *type); + DIType CreatePointerType(Type *type); + DIType CreateVectorType(Type *type); + DIType CreateMemberType(unsigned linnum, Type *type, DIFile file, + const char *c_name, unsigned offset, PROTKIND); + DIType CreateCompositeType(Type *type); + DIType CreateArrayType(Type *type); + DIType CreateSArrayType(Type *type); + DIType CreateAArrayType(Type *type); + DISubroutineType CreateFunctionType(Type *type); + DISubroutineType CreateDelegateType(Type *type); + DIType CreateTypeDescription(Type *type, bool derefclass = false); public: - template - void OpOffset(T &addr, llvm::StructType *type, int index) - { - if (!global.params.symdebug) - return; + template + void OpOffset(T &addr, llvm::StructType *type, int index) { + if (!global.params.symdebug) + return; - uint64_t offset = gDataLayout->getStructLayout(type)->getElementOffset(index); + uint64_t offset = + gDataLayout->getStructLayout(type)->getElementOffset(index); #if LDC_LLVM_VER >= 306 - addr.push_back(llvm::dwarf::DW_OP_plus); - addr.push_back(offset); + addr.push_back(llvm::dwarf::DW_OP_plus); + addr.push_back(offset); #else - llvm::Type *int64Ty = llvm::Type::getInt64Ty(getContext()); - addr.push_back(llvm::ConstantInt::get(int64Ty, llvm::DIBuilder::OpPlus)); - addr.push_back(llvm::ConstantInt::get(int64Ty, offset)); + llvm::Type *int64Ty = llvm::Type::getInt64Ty(getContext()); + addr.push_back(llvm::ConstantInt::get(int64Ty, llvm::DIBuilder::OpPlus)); + addr.push_back(llvm::ConstantInt::get(int64Ty, offset)); #endif - } + } - template - void OpOffset(T &addr, llvm::Value *val, int index) - { - if (!global.params.symdebug) - return; + template void OpOffset(T &addr, llvm::Value *val, int index) { + if (!global.params.symdebug) + return; - llvm::StructType *type = isaStruct(val->getType()->getContainedType(0)); - assert(type); - OpOffset(addr, type, index); - } + llvm::StructType *type = isaStruct(val->getType()->getContainedType(0)); + assert(type); + OpOffset(addr, type, index); + } - template - void OpDeref(T &addr) - { - if (!global.params.symdebug) - return; + template void OpDeref(T &addr) { + if (!global.params.symdebug) + return; #if LDC_LLVM_VER >= 306 - addr.push_back(llvm::dwarf::DW_OP_deref); + addr.push_back(llvm::dwarf::DW_OP_deref); #else - llvm::Type *int64Ty = llvm::Type::getInt64Ty(getContext()); - addr.push_back(llvm::ConstantInt::get(int64Ty, llvm::DIBuilder::OpDeref)); + llvm::Type *int64Ty = llvm::Type::getInt64Ty(getContext()); + addr.push_back(llvm::ConstantInt::get(int64Ty, llvm::DIBuilder::OpDeref)); #endif - } + } }; - } // namespace ldc #endif diff --git a/gen/dvalue.cpp b/gen/dvalue.cpp index c1545e9131..83bca621f4 100644 --- a/gen/dvalue.cpp +++ b/gen/dvalue.cpp @@ -18,108 +18,98 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// -static bool checkVarValueType(LLType* t, bool extraDeref) -{ - if (extraDeref) - { - llvm::PointerType* pt = llvm::dyn_cast(t); - if (!pt) return false; +static bool checkVarValueType(LLType *t, bool extraDeref) { + if (extraDeref) { + llvm::PointerType *pt = llvm::dyn_cast(t); + if (!pt) + return false; - t = pt->getElementType(); - } + t = pt->getElementType(); + } - llvm::PointerType* pt = llvm::dyn_cast(t); - if (!pt) return false; + llvm::PointerType *pt = llvm::dyn_cast(t); + if (!pt) + return false; - // bools should not be stored as i1 any longer. - if (pt->getElementType() == llvm::Type::getInt1Ty(gIR->context())) - return false; + // bools should not be stored as i1 any longer. + if (pt->getElementType() == llvm::Type::getInt1Ty(gIR->context())) + return false; - return true; + return true; } -DVarValue::DVarValue(Type* t, VarDeclaration* vd, LLValue* llvmValue) -: DValue(t), var(vd), val(llvmValue) -{ - assert(checkVarValueType(llvmValue->getType(), isSpecialRefVar(vd))); +DVarValue::DVarValue(Type *t, VarDeclaration *vd, LLValue *llvmValue) + : DValue(t), var(vd), val(llvmValue) { + assert(checkVarValueType(llvmValue->getType(), isSpecialRefVar(vd))); } -DVarValue::DVarValue(Type* t, LLValue* llvmValue) -: DValue(t), var(0), val(llvmValue) -{ - assert(checkVarValueType(llvmValue->getType(), false)); +DVarValue::DVarValue(Type *t, LLValue *llvmValue) + : DValue(t), var(0), val(llvmValue) { + assert(checkVarValueType(llvmValue->getType(), false)); } -LLValue* DVarValue::getLVal() -{ - assert(val); - if (var && isSpecialRefVar(var)) - return DtoLoad(val); - return val; +LLValue *DVarValue::getLVal() { + assert(val); + if (var && isSpecialRefVar(var)) + return DtoLoad(val); + return val; } -LLValue* DVarValue::getRVal() -{ - assert(val); +LLValue *DVarValue::getRVal() { + assert(val); - llvm::Value* storage = val; - if (var && isSpecialRefVar(var)) - storage = DtoLoad(storage); + llvm::Value *storage = val; + if (var && isSpecialRefVar(var)) + storage = DtoLoad(storage); - if (DtoIsPassedByRef(type->toBasetype())) - return storage; + if (DtoIsPassedByRef(type->toBasetype())) + return storage; - llvm::Value* rawValue = DtoLoad(storage); + llvm::Value *rawValue = DtoLoad(storage); - if (type->toBasetype()->ty == Tbool) - { - assert(rawValue->getType() == llvm::Type::getInt8Ty(gIR->context())); - return gIR->ir->CreateTrunc(rawValue, - llvm::Type::getInt1Ty(gIR->context())); - } + if (type->toBasetype()->ty == Tbool) { + assert(rawValue->getType() == llvm::Type::getInt8Ty(gIR->context())); + return gIR->ir->CreateTrunc(rawValue, + llvm::Type::getInt1Ty(gIR->context())); + } - return rawValue; + return rawValue; } -LLValue* DVarValue::getRefStorage() -{ - assert(val); - assert(isSpecialRefVar(var)); - return val; +LLValue *DVarValue::getRefStorage() { + assert(val); + assert(isSpecialRefVar(var)); + return val; } ///////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DSliceValue::getRVal() -{ - assert(len); - assert(ptr); - return DtoAggrPair(len, ptr); +LLValue *DSliceValue::getRVal() { + assert(len); + assert(ptr); + return DtoAggrPair(len, ptr); } ///////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// -DFuncValue::DFuncValue(Type *t, FuncDeclaration* fd, llvm::Value* v, llvm::Value* vt) -: DValue(t), func(fd), val(v), vthis(vt) -{} +DFuncValue::DFuncValue(Type *t, FuncDeclaration *fd, llvm::Value *v, + llvm::Value *vt) + : DValue(t), func(fd), val(v), vthis(vt) {} -DFuncValue::DFuncValue(FuncDeclaration* fd, LLValue* v, LLValue* vt) -: DValue(fd->type), func(fd), val(v), vthis(vt) -{} +DFuncValue::DFuncValue(FuncDeclaration *fd, LLValue *v, LLValue *vt) + : DValue(fd->type), func(fd), val(v), vthis(vt) {} -LLValue* DFuncValue::getRVal() -{ - assert(val); - return val; +LLValue *DFuncValue::getRVal() { + assert(val); + return val; } ///////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DConstValue::getRVal() -{ - assert(c); - return c; +LLValue *DConstValue::getRVal() { + assert(c); + return c; } diff --git a/gen/dvalue.h b/gen/dvalue.h index d14237ba23..97036d1199 100644 --- a/gen/dvalue.h +++ b/gen/dvalue.h @@ -25,11 +25,10 @@ class Dsymbol; class VarDeclaration; class FuncDeclaration; -namespace llvm -{ - class Value; - class Type; - class Constant; +namespace llvm { +class Value; +class Type; +class Constant; } class DImValue; @@ -41,127 +40,136 @@ class DFuncValue; class DSliceValue; // base class for d-values -class DValue -{ +class DValue { public: - Type* type; - DValue(Type* ty) : type(ty) {} - virtual ~DValue() {} + Type *type; + DValue(Type *ty) : type(ty) {} + virtual ~DValue() {} - Type*& getType() { assert(type); return type; } + Type *&getType() { + assert(type); + return type; + } - virtual llvm::Value* getLVal() { assert(0); return 0; } - virtual llvm::Value* getRVal() { assert(0); return 0; } + virtual llvm::Value *getLVal() { + assert(0); + return 0; + } + virtual llvm::Value *getRVal() { + assert(0); + return 0; + } - virtual bool isLVal() { return false; } + virtual bool isLVal() { return false; } - virtual DImValue* isIm() { return NULL; } - virtual DConstValue* isConst() { return NULL; } - virtual DNullValue* isNull() { return NULL; } - virtual DVarValue* isVar() { return NULL; } - virtual DFieldValue* isField() { return NULL; } - virtual DSliceValue* isSlice() { return NULL; } - virtual DFuncValue* isFunc() { return NULL; } + virtual DImValue *isIm() { return NULL; } + virtual DConstValue *isConst() { return NULL; } + virtual DNullValue *isNull() { return NULL; } + virtual DVarValue *isVar() { return NULL; } + virtual DFieldValue *isField() { return NULL; } + virtual DSliceValue *isSlice() { return NULL; } + virtual DFuncValue *isFunc() { return NULL; } protected: - DValue() {} - DValue(const DValue&) { } - DValue& operator=(const DValue& other) { type = other.type; return *this; } + DValue() {} + DValue(const DValue &) {} + DValue &operator=(const DValue &other) { + type = other.type; + return *this; + } }; // immediate d-value -class DImValue : public DValue -{ +class DImValue : public DValue { public: - DImValue(Type* t, llvm::Value* v) : DValue(t), val(v) { } + DImValue(Type *t, llvm::Value *v) : DValue(t), val(v) {} - virtual llvm::Value* getRVal() { assert(val); return val; } + virtual llvm::Value *getRVal() { + assert(val); + return val; + } - virtual DImValue* isIm() { return this; } + virtual DImValue *isIm() { return this; } protected: - llvm::Value* val; + llvm::Value *val; }; // constant d-value -class DConstValue : public DValue -{ +class DConstValue : public DValue { public: - DConstValue(Type* t, llvm::Constant* con) : DValue(t), c(con) {} + DConstValue(Type *t, llvm::Constant *con) : DValue(t), c(con) {} - virtual llvm::Value* getRVal(); + virtual llvm::Value *getRVal(); - virtual DConstValue* isConst() { return this; } + virtual DConstValue *isConst() { return this; } - llvm::Constant* c; + llvm::Constant *c; }; // null d-value -class DNullValue : public DConstValue -{ +class DNullValue : public DConstValue { public: - DNullValue(Type* t, llvm::Constant* con) : DConstValue(t,con) {} - virtual DNullValue* isNull() { return this; } + DNullValue(Type *t, llvm::Constant *con) : DConstValue(t, con) {} + virtual DNullValue *isNull() { return this; } }; // variable d-value -class DVarValue : public DValue -{ +class DVarValue : public DValue { public: - DVarValue(Type* t, VarDeclaration* vd, llvm::Value* llvmValue); - DVarValue(Type* t, llvm::Value* llvmValue); + DVarValue(Type *t, VarDeclaration *vd, llvm::Value *llvmValue); + DVarValue(Type *t, llvm::Value *llvmValue); - virtual bool isLVal() { return true; } - virtual llvm::Value* getLVal(); - virtual llvm::Value* getRVal(); + virtual bool isLVal() { return true; } + virtual llvm::Value *getLVal(); + virtual llvm::Value *getRVal(); - /// Returns the underlying storage for special internal ref variables. - /// Illegal to call on any other value. - virtual llvm::Value* getRefStorage(); + /// Returns the underlying storage for special internal ref variables. + /// Illegal to call on any other value. + virtual llvm::Value *getRefStorage(); - virtual DVarValue* isVar() { return this; } + virtual DVarValue *isVar() { return this; } + + VarDeclaration *var; - VarDeclaration* var; protected: - llvm::Value* val; + llvm::Value *val; }; // field d-value -class DFieldValue : public DVarValue -{ +class DFieldValue : public DVarValue { public: - DFieldValue(Type* t, llvm::Value* llvmValue) : DVarValue(t, llvmValue) {} - virtual DFieldValue* isField() { return this; } + DFieldValue(Type *t, llvm::Value *llvmValue) : DVarValue(t, llvmValue) {} + virtual DFieldValue *isField() { return this; } }; // slice d-value -class DSliceValue : public DValue -{ +class DSliceValue : public DValue { public: - DSliceValue(Type* t, llvm::Value* l, llvm::Value* p) : DValue(t), len(l), ptr(p) {} + DSliceValue(Type *t, llvm::Value *l, llvm::Value *p) + : DValue(t), len(l), ptr(p) {} - virtual llvm::Value* getRVal(); + virtual llvm::Value *getRVal(); - virtual DSliceValue* isSlice() { return this; } + virtual DSliceValue *isSlice() { return this; } - llvm::Value* len; - llvm::Value* ptr; + llvm::Value *len; + llvm::Value *ptr; }; // function d-value -class DFuncValue : public DValue -{ +class DFuncValue : public DValue { public: - DFuncValue(Type *t, FuncDeclaration* fd, llvm::Value* v, llvm::Value* vt = 0); - DFuncValue(FuncDeclaration* fd, llvm::Value* v, llvm::Value* vt = 0); + DFuncValue(Type *t, FuncDeclaration *fd, llvm::Value *v, llvm::Value *vt = 0); + DFuncValue(FuncDeclaration *fd, llvm::Value *v, llvm::Value *vt = 0); - virtual llvm::Value* getRVal(); + virtual llvm::Value *getRVal(); - virtual DFuncValue* isFunc() { return this; } + virtual DFuncValue *isFunc() { return this; } - FuncDeclaration* func; - llvm::Value* val; - llvm::Value* vthis; + FuncDeclaration *func; + llvm::Value *val; + llvm::Value *vthis; }; #endif // LDC_GEN_DVALUE_H diff --git a/gen/functions.cpp b/gen/functions.cpp index d3f3542356..f61fba935d 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -38,1168 +38,1074 @@ #include "llvm/IR/CFG.h" #include -llvm::FunctionType* DtoFunctionType(Type* type, IrFuncTy &irFty, Type* thistype, Type* nesttype, - bool isMain, bool isCtor, bool isIntrinsic) -{ - IF_LOG Logger::println("DtoFunctionType(%s)", type->toChars()); - LOG_SCOPE +llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, + Type *nesttype, bool isMain, bool isCtor, + bool isIntrinsic) { + IF_LOG Logger::println("DtoFunctionType(%s)", type->toChars()); + LOG_SCOPE - // sanity check - assert(type->ty == Tfunction); - TypeFunction* f = static_cast(type); - assert(f->next && "Encountered function type with invalid return type; " - "trying to codegen function ignored by the frontend?"); + // sanity check + assert(type->ty == Tfunction); + TypeFunction *f = static_cast(type); + assert(f->next && "Encountered function type with invalid return type; " + "trying to codegen function ignored by the frontend?"); - // Return cached type if available - if (irFty.funcType) return irFty.funcType; + // Return cached type if available + if (irFty.funcType) + return irFty.funcType; - TargetABI* abi = (isIntrinsic ? TargetABI::getIntrinsic() : gABI); + TargetABI *abi = (isIntrinsic ? TargetABI::getIntrinsic() : gABI); - // Do not modify irFty yet; this function may be called recursively if any - // of the argument types refer to this type. - IrFuncTy newIrFty; + // Do not modify irFty yet; this function may be called recursively if any + // of the argument types refer to this type. + IrFuncTy newIrFty; - // The index of the next argument on the LLVM level. - unsigned nextLLArgIdx = 0; + // The index of the next argument on the LLVM level. + unsigned nextLLArgIdx = 0; - if (isMain) - { - // _Dmain always returns i32, no matter what the type in the D main() is. - newIrFty.ret = new IrFuncTyArg(Type::tint32, false); + if (isMain) { + // _Dmain always returns i32, no matter what the type in the D main() is. + newIrFty.ret = new IrFuncTyArg(Type::tint32, false); + } else { + Type *rt = f->next; + const bool byref = f->isref && rt->toBasetype()->ty != Tvoid; + AttrBuilder attrBuilder; + + if (abi->returnInArg(f)) { + // sret return + newIrFty.arg_sret = new IrFuncTyArg( + rt, true, + AttrBuilder().add(LLAttribute::StructRet).add(LLAttribute::NoAlias)); + rt = Type::tvoid; + ++nextLLArgIdx; + } else { + // sext/zext return + attrBuilder.add(DtoShouldExtend(byref ? rt->pointerTo() : rt)); } - else - { - Type* rt = f->next; - const bool byref = f->isref && rt->toBasetype()->ty != Tvoid; - AttrBuilder attrBuilder; + newIrFty.ret = new IrFuncTyArg(rt, byref, attrBuilder); + } + ++nextLLArgIdx; - if (abi->returnInArg(f)) - { - // sret return - newIrFty.arg_sret = new IrFuncTyArg(rt, true, - AttrBuilder().add(LLAttribute::StructRet).add(LLAttribute::NoAlias)); - rt = Type::tvoid; - ++nextLLArgIdx; - } - else - { - // sext/zext return - attrBuilder.add(DtoShouldExtend(byref ? rt->pointerTo() : rt)); - } - newIrFty.ret = new IrFuncTyArg(rt, byref, attrBuilder); - } + if (thistype) { + // Add the this pointer for member functions + AttrBuilder attrBuilder; + if (isCtor) + attrBuilder.add(LLAttribute::Returned); + newIrFty.arg_this = new IrFuncTyArg( + thistype, thistype->toBasetype()->ty == Tstruct, attrBuilder); ++nextLLArgIdx; + } else if (nesttype) { + // Add the context pointer for nested functions + newIrFty.arg_nest = new IrFuncTyArg(nesttype, false); + ++nextLLArgIdx; + } - if (thistype) - { - // Add the this pointer for member functions - AttrBuilder attrBuilder; - if (isCtor) - attrBuilder.add(LLAttribute::Returned); - newIrFty.arg_this = new IrFuncTyArg(thistype, thistype->toBasetype()->ty == Tstruct, attrBuilder); - ++nextLLArgIdx; - } - else if (nesttype) - { - // Add the context pointer for nested functions - newIrFty.arg_nest = new IrFuncTyArg(nesttype, false); + // vararg functions are special too + if (f->varargs) { + if (f->linkage == LINKd) { + // d style with hidden args + // 2 (array) is handled by the frontend + if (f->varargs == 1) { + // _arguments + newIrFty.arg_arguments = + new IrFuncTyArg(Type::dtypeinfo->type->arrayOf(), false); ++nextLLArgIdx; + } } - // vararg functions are special too - if (f->varargs) - { - if (f->linkage == LINKd) - { - // d style with hidden args - // 2 (array) is handled by the frontend - if (f->varargs == 1) - { - // _arguments - newIrFty.arg_arguments = new IrFuncTyArg(Type::dtypeinfo->type->arrayOf(), false); - ++nextLLArgIdx; - } - } + newIrFty.c_vararg = true; + } - newIrFty.c_vararg = true; + // if this _Dmain() doesn't have an argument, we force it to have one + const size_t numExplicitDArgs = Parameter::dim(f->parameters); + + if (isMain && numExplicitDArgs == 0) { + Type *mainargs = Type::tchar->arrayOf()->arrayOf(); + newIrFty.args.push_back(new IrFuncTyArg(mainargs, false)); + ++nextLLArgIdx; + } + + for (size_t i = 0; i < numExplicitDArgs; ++i) { + Parameter *arg = Parameter::getNth(f->parameters, i); + + // Whether the parameter is passed by LLVM value or as a pointer to the + // alloca/…. + bool passPointer = arg->storageClass & (STCref | STCout); + + Type *loweredDType = arg->type; + AttrBuilder attrBuilder; + if (arg->storageClass & STClazy) { + // Lazy arguments are lowered to delegates. + Logger::println("lazy param"); + TypeFunction *ltf = new TypeFunction(NULL, arg->type, 0, LINKd); + TypeDelegate *ltd = new TypeDelegate(ltf); + loweredDType = ltd; + } else if (!passPointer) { + if (abi->passByVal(loweredDType)) { + attrBuilder.add(LLAttribute::ByVal); + // byval parameters are also passed as an address + passPointer = true; + } else { + // Add sext/zext as needed. + attrBuilder.add(DtoShouldExtend(loweredDType)); + } } + newIrFty.args.push_back( + new IrFuncTyArg(loweredDType, passPointer, attrBuilder)); + newIrFty.args.back()->parametersIdx = i; + ++nextLLArgIdx; + } - // if this _Dmain() doesn't have an argument, we force it to have one - const size_t numExplicitDArgs = Parameter::dim(f->parameters); + // let the abi rewrite the types as necesary + abi->rewriteFunctionType(f, newIrFty); - if (isMain && numExplicitDArgs == 0) - { - Type* mainargs = Type::tchar->arrayOf()->arrayOf(); - newIrFty.args.push_back(new IrFuncTyArg(mainargs, false)); - ++nextLLArgIdx; - } + // Now we can modify irFty safely. + irFty = llvm_move(newIrFty); - for (size_t i = 0; i < numExplicitDArgs; ++i) - { - Parameter* arg = Parameter::getNth(f->parameters, i); + // Finally build the actual LLVM function type. + llvm::SmallVector argtypes; + argtypes.reserve(nextLLArgIdx); - // Whether the parameter is passed by LLVM value or as a pointer to the - // alloca/…. - bool passPointer = arg->storageClass & (STCref | STCout); + if (irFty.arg_sret) + argtypes.push_back(irFty.arg_sret->ltype); + if (irFty.arg_this) + argtypes.push_back(irFty.arg_this->ltype); + if (irFty.arg_nest) + argtypes.push_back(irFty.arg_nest->ltype); + if (irFty.arg_arguments) + argtypes.push_back(irFty.arg_arguments->ltype); - Type* loweredDType = arg->type; - AttrBuilder attrBuilder; - if (arg->storageClass & STClazy) - { - // Lazy arguments are lowered to delegates. - Logger::println("lazy param"); - TypeFunction *ltf = new TypeFunction(NULL, arg->type, 0, LINKd); - TypeDelegate *ltd = new TypeDelegate(ltf); - loweredDType = ltd; - } - else if (!passPointer) - { - if (abi->passByVal(loweredDType)) - { - attrBuilder.add(LLAttribute::ByVal); - // byval parameters are also passed as an address - passPointer = true; - } - else - { - // Add sext/zext as needed. - attrBuilder.add(DtoShouldExtend(loweredDType)); - } - } - newIrFty.args.push_back(new IrFuncTyArg(loweredDType, passPointer, attrBuilder)); - newIrFty.args.back()->parametersIdx = i; - ++nextLLArgIdx; - } + if (irFty.arg_sret && irFty.arg_this && abi->passThisBeforeSret(f)) + std::swap(argtypes[0], argtypes[1]); - // let the abi rewrite the types as necesary - abi->rewriteFunctionType(f, newIrFty); + const size_t firstExplicitArg = argtypes.size(); + const size_t numExplicitLLArgs = irFty.args.size(); + for (size_t i = 0; i < numExplicitLLArgs; i++) { + argtypes.push_back(irFty.args[i]->ltype); + } - // Now we can modify irFty safely. - irFty = llvm_move(newIrFty); + // reverse params? + if (irFty.reverseParams && numExplicitLLArgs > 1) { + std::reverse(argtypes.begin() + firstExplicitArg, argtypes.end()); + } - // Finally build the actual LLVM function type. - llvm::SmallVector argtypes; - argtypes.reserve(nextLLArgIdx); + irFty.funcType = + LLFunctionType::get(irFty.ret->ltype, argtypes, irFty.c_vararg); - if (irFty.arg_sret) argtypes.push_back(irFty.arg_sret->ltype); - if (irFty.arg_this) argtypes.push_back(irFty.arg_this->ltype); - if (irFty.arg_nest) argtypes.push_back(irFty.arg_nest->ltype); - if (irFty.arg_arguments) argtypes.push_back(irFty.arg_arguments->ltype); + IF_LOG Logger::cout() << "Final function type: " << *irFty.funcType << "\n"; - if (irFty.arg_sret && irFty.arg_this && abi->passThisBeforeSret(f)) - std::swap(argtypes[0], argtypes[1]); - - const size_t firstExplicitArg = argtypes.size(); - const size_t numExplicitLLArgs = irFty.args.size(); - for (size_t i = 0; i < numExplicitLLArgs; i++) - { - argtypes.push_back(irFty.args[i]->ltype); - } - - // reverse params? - if (irFty.reverseParams && numExplicitLLArgs > 1) - { - std::reverse(argtypes.begin() + firstExplicitArg, argtypes.end()); - } - - irFty.funcType = LLFunctionType::get(irFty.ret->ltype, argtypes, irFty.c_vararg); - - IF_LOG Logger::cout() << "Final function type: " << *irFty.funcType << "\n"; - - return irFty.funcType; + return irFty.funcType; } ////////////////////////////////////////////////////////////////////////////////////////// -static llvm::FunctionType* DtoVaFunctionType(FuncDeclaration* fdecl) -{ - IrFuncTy &irFty = getIrFunc(fdecl, true)->irFty; - if (irFty.funcType) return irFty.funcType; +static llvm::FunctionType *DtoVaFunctionType(FuncDeclaration *fdecl) { + IrFuncTy &irFty = getIrFunc(fdecl, true)->irFty; + if (irFty.funcType) + return irFty.funcType; - irFty.ret = new IrFuncTyArg(Type::tvoid, false); + irFty.ret = new IrFuncTyArg(Type::tvoid, false); + irFty.args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false)); + + if (fdecl->llvmInternal == LLVMva_start) + irFty.funcType = GET_INTRINSIC_DECL(vastart)->getFunctionType(); + else if (fdecl->llvmInternal == LLVMva_copy) { + irFty.funcType = GET_INTRINSIC_DECL(vacopy)->getFunctionType(); irFty.args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false)); + } else if (fdecl->llvmInternal == LLVMva_end) + irFty.funcType = GET_INTRINSIC_DECL(vaend)->getFunctionType(); + assert(irFty.funcType); - if (fdecl->llvmInternal == LLVMva_start) - irFty.funcType = GET_INTRINSIC_DECL(vastart)->getFunctionType(); - else if (fdecl->llvmInternal == LLVMva_copy) { - irFty.funcType = GET_INTRINSIC_DECL(vacopy)->getFunctionType(); - irFty.args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false)); - } - else if (fdecl->llvmInternal == LLVMva_end) - irFty.funcType = GET_INTRINSIC_DECL(vaend)->getFunctionType(); - assert(irFty.funcType); - - return irFty.funcType; + return irFty.funcType; } ////////////////////////////////////////////////////////////////////////////////////////// -llvm::FunctionType* DtoFunctionType(FuncDeclaration* fdecl) -{ - // handle for C vararg intrinsics - if (DtoIsVaIntrinsic(fdecl)) - return DtoVaFunctionType(fdecl); +llvm::FunctionType *DtoFunctionType(FuncDeclaration *fdecl) { + // handle for C vararg intrinsics + if (DtoIsVaIntrinsic(fdecl)) + return DtoVaFunctionType(fdecl); - Type *dthis=0, *dnest=0; + Type *dthis = 0, *dnest = 0; - if (fdecl->ident == Id::ensure || fdecl->ident == Id::require) { - FuncDeclaration *p = fdecl->parent->isFuncDeclaration(); - assert(p); - AggregateDeclaration *ad = p->isMember2(); - assert(ad); - dnest = Type::tvoid->pointerTo(); - } else - if (fdecl->needThis()) { - if (AggregateDeclaration* ad = fdecl->isMember2()) { - IF_LOG Logger::println("isMember = this is: %s", ad->type->toChars()); - dthis = ad->type; - LLType* thisty = DtoType(dthis); - //Logger::cout() << "this llvm type: " << *thisty << '\n'; - if (ad->isStructDeclaration()) - thisty = getPtrToType(thisty); - } - else { - IF_LOG Logger::println("chars: %s type: %s kind: %s", fdecl->toChars(), fdecl->type->toChars(), fdecl->kind()); - llvm_unreachable("needThis, but invalid parent declaration."); - } - } - else if (fdecl->isNested()) { - dnest = Type::tvoid->pointerTo(); + if (fdecl->ident == Id::ensure || fdecl->ident == Id::require) { + FuncDeclaration *p = fdecl->parent->isFuncDeclaration(); + assert(p); + AggregateDeclaration *ad = p->isMember2(); + assert(ad); + dnest = Type::tvoid->pointerTo(); + } else if (fdecl->needThis()) { + if (AggregateDeclaration *ad = fdecl->isMember2()) { + IF_LOG Logger::println("isMember = this is: %s", ad->type->toChars()); + dthis = ad->type; + LLType *thisty = DtoType(dthis); + // Logger::cout() << "this llvm type: " << *thisty << '\n'; + if (ad->isStructDeclaration()) + thisty = getPtrToType(thisty); + } else { + IF_LOG Logger::println("chars: %s type: %s kind: %s", fdecl->toChars(), + fdecl->type->toChars(), fdecl->kind()); + llvm_unreachable("needThis, but invalid parent declaration."); } + } else if (fdecl->isNested()) { + dnest = Type::tvoid->pointerTo(); + } - LLFunctionType* functype = DtoFunctionType(fdecl->type, getIrFunc(fdecl, true)->irFty, dthis, dnest, - fdecl->isMain(), fdecl->isCtorDeclaration(), - DtoIsIntrinsic(fdecl)); + LLFunctionType *functype = DtoFunctionType( + fdecl->type, getIrFunc(fdecl, true)->irFty, dthis, dnest, fdecl->isMain(), + fdecl->isCtorDeclaration(), DtoIsIntrinsic(fdecl)); - return functype; + return functype; } ////////////////////////////////////////////////////////////////////////////////////////// -static llvm::Function* DtoDeclareVaFunction(FuncDeclaration* fdecl) -{ - DtoVaFunctionType(fdecl); - llvm::Function* func = 0; +static llvm::Function *DtoDeclareVaFunction(FuncDeclaration *fdecl) { + DtoVaFunctionType(fdecl); + llvm::Function *func = 0; - if (fdecl->llvmInternal == LLVMva_start) - func = GET_INTRINSIC_DECL(vastart); - else if (fdecl->llvmInternal == LLVMva_copy) - func = GET_INTRINSIC_DECL(vacopy); - else if (fdecl->llvmInternal == LLVMva_end) - func = GET_INTRINSIC_DECL(vaend); - assert(func); + if (fdecl->llvmInternal == LLVMva_start) + func = GET_INTRINSIC_DECL(vastart); + else if (fdecl->llvmInternal == LLVMva_copy) + func = GET_INTRINSIC_DECL(vacopy); + else if (fdecl->llvmInternal == LLVMva_end) + func = GET_INTRINSIC_DECL(vaend); + assert(func); - getIrFunc(fdecl)->func = func; - return func; + getIrFunc(fdecl)->func = func; + return func; } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoResolveFunction(FuncDeclaration* fdecl) -{ - if ((!global.params.useUnitTests || !fdecl->type) && fdecl->isUnitTestDeclaration()) { - IF_LOG Logger::println("Ignoring unittest %s", fdecl->toPrettyChars()); - return; // ignore declaration completely - } +void DtoResolveFunction(FuncDeclaration *fdecl) { + if ((!global.params.useUnitTests || !fdecl->type) && + fdecl->isUnitTestDeclaration()) { + IF_LOG Logger::println("Ignoring unittest %s", fdecl->toPrettyChars()); + return; // ignore declaration completely + } - if (fdecl->ir.isResolved()) return; - fdecl->ir.setResolved(); + if (fdecl->ir.isResolved()) + return; + fdecl->ir.setResolved(); - Type *type = fdecl->type; - // If errors occurred compiling it, such as bugzilla 6118 - if (type && type->ty == Tfunction) { - Type *next = static_cast(type)->next; - if (!next || next->ty == Terror) - return; - } + Type *type = fdecl->type; + // If errors occurred compiling it, such as bugzilla 6118 + if (type && type->ty == Tfunction) { + Type *next = static_cast(type)->next; + if (!next || next->ty == Terror) + return; + } - //printf("resolve function: %s\n", fdecl->toPrettyChars()); + // printf("resolve function: %s\n", fdecl->toPrettyChars()); - if (fdecl->parent) - if (TemplateInstance* tinst = fdecl->parent->isTemplateInstance()) - { - if (TemplateDeclaration* tempdecl = tinst->tempdecl->isTemplateDeclaration()) - { - if (tempdecl->llvmInternal == LLVMva_arg) - { - Logger::println("magic va_arg found"); - fdecl->llvmInternal = LLVMva_arg; - fdecl->ir.setDefined(); - return; // this gets mapped to an instruction so a declaration makes no sence - } - else if (tempdecl->llvmInternal == LLVMva_start) - { - Logger::println("magic va_start found"); - fdecl->llvmInternal = LLVMva_start; - } - else if (tempdecl->llvmInternal == LLVMintrinsic) - { - Logger::println("overloaded intrinsic found"); - assert(fdecl->llvmInternal == LLVMintrinsic); - assert(fdecl->mangleOverride); - } - else if (tempdecl->llvmInternal == LLVMinline_asm) - { - Logger::println("magic inline asm found"); - TypeFunction* tf = static_cast(fdecl->type); - if (tf->varargs != 1 || (fdecl->parameters && fdecl->parameters->dim != 0)) - { - tempdecl->error("invalid __asm declaration, must be a D style variadic with no explicit parameters"); - fatal(); - } - fdecl->llvmInternal = LLVMinline_asm; - fdecl->ir.setDefined(); - return; // this gets mapped to a special inline asm call, no point in going on. - } - else if (tempdecl->llvmInternal == LLVMinline_ir) - { - Logger::println("magic inline ir found"); - fdecl->llvmInternal = LLVMinline_ir; - fdecl->linkage = LINKc; - Type* type = fdecl->type; - assert(type->ty == Tfunction); - static_cast(type)->linkage = LINKc; + if (fdecl->parent) + if (TemplateInstance *tinst = fdecl->parent->isTemplateInstance()) { + if (TemplateDeclaration *tempdecl = + tinst->tempdecl->isTemplateDeclaration()) { + if (tempdecl->llvmInternal == LLVMva_arg) { + Logger::println("magic va_arg found"); + fdecl->llvmInternal = LLVMva_arg; + fdecl->ir.setDefined(); + return; // this gets mapped to an instruction so a declaration makes + // no sence + } else if (tempdecl->llvmInternal == LLVMva_start) { + Logger::println("magic va_start found"); + fdecl->llvmInternal = LLVMva_start; + } else if (tempdecl->llvmInternal == LLVMintrinsic) { + Logger::println("overloaded intrinsic found"); + assert(fdecl->llvmInternal == LLVMintrinsic); + assert(fdecl->mangleOverride); + } else if (tempdecl->llvmInternal == LLVMinline_asm) { + Logger::println("magic inline asm found"); + TypeFunction *tf = static_cast(fdecl->type); + if (tf->varargs != 1 || + (fdecl->parameters && fdecl->parameters->dim != 0)) { + tempdecl->error("invalid __asm declaration, must be a D style " + "variadic with no explicit parameters"); + fatal(); + } + fdecl->llvmInternal = LLVMinline_asm; + fdecl->ir.setDefined(); + return; // this gets mapped to a special inline asm call, no point in + // going on. + } else if (tempdecl->llvmInternal == LLVMinline_ir) { + Logger::println("magic inline ir found"); + fdecl->llvmInternal = LLVMinline_ir; + fdecl->linkage = LINKc; + Type *type = fdecl->type; + assert(type->ty == Tfunction); + static_cast(type)->linkage = LINKc; - DtoFunctionType(fdecl); - DtoDeclareFunction(fdecl); - fdecl->ir.setDefined(); - return; - } + DtoFunctionType(fdecl); + DtoDeclareFunction(fdecl); + fdecl->ir.setDefined(); + return; } + } } - DtoFunctionType(fdecl); + DtoFunctionType(fdecl); - IF_LOG Logger::println("DtoResolveFunction(%s): %s", fdecl->toPrettyChars(), fdecl->loc.toChars()); - LOG_SCOPE; + IF_LOG Logger::println("DtoResolveFunction(%s): %s", fdecl->toPrettyChars(), + fdecl->loc.toChars()); + LOG_SCOPE; - // queue declaration unless the function is abstract without body - if (!fdecl->isAbstract() || fdecl->fbody) - { - DtoDeclareFunction(fdecl); - } + // queue declaration unless the function is abstract without body + if (!fdecl->isAbstract() || fdecl->fbody) { + DtoDeclareFunction(fdecl); + } } ////////////////////////////////////////////////////////////////////////////////////////// -static void set_param_attrs(TypeFunction* f, llvm::Function* func, FuncDeclaration* fdecl) -{ - IrFuncTy &irFty = getIrFunc(fdecl)->irFty; - AttrSet newAttrs = AttrSet::extractFunctionAndReturnAttributes(func); +static void set_param_attrs(TypeFunction *f, llvm::Function *func, + FuncDeclaration *fdecl) { + IrFuncTy &irFty = getIrFunc(fdecl)->irFty; + AttrSet newAttrs = AttrSet::extractFunctionAndReturnAttributes(func); - int idx = 0; + int idx = 0; - // handle implicit args - #define ADD_PA(X) \ - if (irFty.X) { \ - newAttrs.add(idx, irFty.X->attrs); \ - idx++; \ - } +// handle implicit args +#define ADD_PA(X) \ + if (irFty.X) { \ + newAttrs.add(idx, irFty.X->attrs); \ + idx++; \ + } - ADD_PA(ret) + ADD_PA(ret) - if (irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(f)) - { - ADD_PA(arg_this) - ADD_PA(arg_sret) - } - else - { - ADD_PA(arg_sret) - ADD_PA(arg_this) - } + if (irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(f)) { + ADD_PA(arg_this) + ADD_PA(arg_sret) + } else { + ADD_PA(arg_sret) + ADD_PA(arg_this) + } - ADD_PA(arg_nest) - ADD_PA(arg_arguments) + ADD_PA(arg_nest) + ADD_PA(arg_arguments) - #undef ADD_PA +#undef ADD_PA - // Set attributes on the explicit parameters. - const size_t n = irFty.args.size(); - for (size_t k = 0; k < n; k++) - { - const size_t i = idx + (irFty.reverseParams ? (n - k - 1) : k); - newAttrs.add(i, irFty.args[k]->attrs); - } + // Set attributes on the explicit parameters. + const size_t n = irFty.args.size(); + for (size_t k = 0; k < n; k++) { + const size_t i = idx + (irFty.reverseParams ? (n - k - 1) : k); + newAttrs.add(i, irFty.args[k]->attrs); + } - // Store the final attribute set - func->setAttributes(newAttrs); + // Store the final attribute set + func->setAttributes(newAttrs); } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoDeclareFunction(FuncDeclaration* fdecl) -{ - DtoResolveFunction(fdecl); +void DtoDeclareFunction(FuncDeclaration *fdecl) { + DtoResolveFunction(fdecl); - if (fdecl->ir.isDeclared()) return; - fdecl->ir.setDeclared(); + if (fdecl->ir.isDeclared()) + return; + fdecl->ir.setDeclared(); - IF_LOG Logger::println("DtoDeclareFunction(%s): %s", fdecl->toPrettyChars(), fdecl->loc.toChars()); - LOG_SCOPE; + IF_LOG Logger::println("DtoDeclareFunction(%s): %s", fdecl->toPrettyChars(), + fdecl->loc.toChars()); + LOG_SCOPE; - if (fdecl->isUnitTestDeclaration() && !global.params.useUnitTests) - { - Logger::println("unit tests not enabled"); - return; + if (fdecl->isUnitTestDeclaration() && !global.params.useUnitTests) { + Logger::println("unit tests not enabled"); + return; + } + + // printf("declare function: %s\n", fdecl->toPrettyChars()); + + // intrinsic sanity check + if (DtoIsIntrinsic(fdecl) && fdecl->fbody) { + error(fdecl->loc, "intrinsics cannot have function bodies"); + fatal(); + } + + // get TypeFunction* + Type *t = fdecl->type->toBasetype(); + TypeFunction *f = static_cast(t); + + // create IrFunction + IrFunction *irFunc = getIrFunc(fdecl, true); + + LLFunction *vafunc = 0; + if (DtoIsVaIntrinsic(fdecl)) + vafunc = DtoDeclareVaFunction(fdecl); + + // calling convention + LINK link = f->linkage; + if (vafunc || DtoIsIntrinsic(fdecl) + // DMD treats _Dmain as having C calling convention and this has been + // hardcoded into druntime, even if the frontend type has D linkage. + // See Bugzilla issue 9028. + || fdecl->isMain()) { + link = LINKc; + } + + // mangled name + std::string mangledName(mangleExact(fdecl)); + mangledName = gABI->mangleForLLVM(mangledName, link); + + // construct function + LLFunctionType *functype = DtoFunctionType(fdecl); + LLFunction *func = vafunc ? vafunc : gIR->module.getFunction(mangledName); + if (!func) { + if (fdecl->llvmInternal == LLVMinline_ir) { + func = DtoInlineIRFunction(fdecl); + } else { + // All function declarations are "external" - any other linkage type + // is set when actually defining the function. + func = LLFunction::Create(functype, llvm::GlobalValue::ExternalLinkage, + mangledName, &gIR->module); + } + } else if (func->getFunctionType() != functype) { + error(fdecl->loc, "Function type does not match previously declared " + "function with the same mangled name: %s", + mangleExact(fdecl)); + fatal(); + } + + func->setCallingConv(gABI->callingConv(func->getFunctionType(), link)); + + IF_LOG Logger::cout() << "func = " << *func << std::endl; + + // add func to IRFunc + irFunc->func = func; + + // parameter attributes + if (!DtoIsIntrinsic(fdecl)) { + set_param_attrs(f, func, fdecl); + if (global.params.disableRedZone) { + func->addFnAttr(LLAttribute::NoRedZone); + } + } + + // main + if (fdecl->isMain()) { + // Detect multiple main functions, which is disallowed. DMD checks this + // in the glue code, so we need to do it here as well. + if (gIR->mainFunc) { + error(fdecl->loc, "only one main function allowed"); + } + gIR->mainFunc = func; + } + + if (fdecl->neverInline) { + irFunc->setNeverInline(); + } + + if (fdecl->llvmInternal == LLVMglobal_crt_ctor || + fdecl->llvmInternal == LLVMglobal_crt_dtor) { + AppendFunctionToLLVMGlobalCtorsDtors( + func, fdecl->priority, fdecl->llvmInternal == LLVMglobal_crt_ctor); + } + + IrFuncTy &irFty = irFunc->irFty; + + // name parameters + llvm::Function::arg_iterator iarg = func->arg_begin(); + + const bool passThisBeforeSret = + irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(f); + + if (irFty.arg_sret && !passThisBeforeSret) { + iarg->setName(".sret_arg"); + irFunc->retArg = iarg; + ++iarg; + } + + if (irFty.arg_this) { + iarg->setName(".this_arg"); + irFunc->thisArg = iarg; + + VarDeclaration *v = fdecl->vthis; + if (v) { + // We already build the this argument here if we will need it + // later for codegen'ing the function, just as normal + // parameters below, because it can be referred to in nested + // context types. Will be given storage in DtoDefineFunction. + assert(!isIrParameterCreated(v)); + IrParameter *irParam = getIrParameter(v, true); + irParam->value = iarg; + irParam->arg = irFty.arg_this; + irParam->isVthis = true; } - //printf("declare function: %s\n", fdecl->toPrettyChars()); + ++iarg; + } else if (irFty.arg_nest) { + iarg->setName(".nest_arg"); + irFunc->nestArg = iarg; + assert(irFunc->nestArg); + ++iarg; + } - // intrinsic sanity check - if (DtoIsIntrinsic(fdecl) && fdecl->fbody) { - error(fdecl->loc, "intrinsics cannot have function bodies"); - fatal(); + if (passThisBeforeSret) { + iarg->setName(".sret_arg"); + irFunc->retArg = iarg; + ++iarg; + } + + if (irFty.arg_arguments) { + iarg->setName("._arguments"); + irFunc->_arguments = iarg; + ++iarg; + } + + unsigned int k = 0; + for (; iarg != func->arg_end(); ++iarg) { + size_t llExplicitIdx = irFty.reverseParams ? irFty.args.size() - k - 1 : k; + ++k; + IrFuncTyArg *arg = irFty.args[llExplicitIdx]; + + if (!fdecl->parameters || arg->parametersIdx >= fdecl->parameters->dim) { + iarg->setName("unnamed"); + continue; } - // get TypeFunction* - Type* t = fdecl->type->toBasetype(); - TypeFunction* f = static_cast(t); + Dsymbol *const argsym = (*fdecl->parameters)[arg->parametersIdx]; + VarDeclaration *argvd = argsym->isVarDeclaration(); + assert(argvd); - // create IrFunction - IrFunction *irFunc = getIrFunc(fdecl, true); + iarg->setName(argvd->ident->toChars() + llvm::Twine("_arg")); - LLFunction* vafunc = 0; - if (DtoIsVaIntrinsic(fdecl)) - vafunc = DtoDeclareVaFunction(fdecl); - - // calling convention - LINK link = f->linkage; - if (vafunc || DtoIsIntrinsic(fdecl) - // DMD treats _Dmain as having C calling convention and this has been - // hardcoded into druntime, even if the frontend type has D linkage. - // See Bugzilla issue 9028. - || fdecl->isMain() - ) - { - link = LINKc; - } - - // mangled name - std::string mangledName(mangleExact(fdecl)); - mangledName = gABI->mangleForLLVM(mangledName, link); - - // construct function - LLFunctionType* functype = DtoFunctionType(fdecl); - LLFunction* func = vafunc ? vafunc : gIR->module.getFunction(mangledName); - if (!func) { - if(fdecl->llvmInternal == LLVMinline_ir) - { - func = DtoInlineIRFunction(fdecl); - } - else - { - // All function declarations are "external" - any other linkage type - // is set when actually defining the function. - func = LLFunction::Create(functype, - llvm::GlobalValue::ExternalLinkage, mangledName, &gIR->module); - } - } else if (func->getFunctionType() != functype) { - error(fdecl->loc, "Function type does not match previously declared function with the same mangled name: %s", mangleExact(fdecl)); - fatal(); - } - - func->setCallingConv(gABI->callingConv(func->getFunctionType(), link)); - - IF_LOG Logger::cout() << "func = " << *func << std::endl; - - // add func to IRFunc - irFunc->func = func; - - // parameter attributes - if (!DtoIsIntrinsic(fdecl)) { - set_param_attrs(f, func, fdecl); - if (global.params.disableRedZone) { - func->addFnAttr(LLAttribute::NoRedZone); - } - } - - // main - if (fdecl->isMain()) { - // Detect multiple main functions, which is disallowed. DMD checks this - // in the glue code, so we need to do it here as well. - if (gIR->mainFunc) { - error(fdecl->loc, "only one main function allowed"); - } - gIR->mainFunc = func; - } - - if (fdecl->neverInline) - { - irFunc->setNeverInline(); - } - - if (fdecl->llvmInternal == LLVMglobal_crt_ctor || fdecl->llvmInternal == LLVMglobal_crt_dtor) - { - AppendFunctionToLLVMGlobalCtorsDtors(func, fdecl->priority, fdecl->llvmInternal == LLVMglobal_crt_ctor); - } - - IrFuncTy &irFty = irFunc->irFty; - - // name parameters - llvm::Function::arg_iterator iarg = func->arg_begin(); - - const bool passThisBeforeSret = irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(f); - - if (irFty.arg_sret && !passThisBeforeSret) { - iarg->setName(".sret_arg"); - irFunc->retArg = iarg; - ++iarg; - } - - if (irFty.arg_this) { - iarg->setName(".this_arg"); - irFunc->thisArg = iarg; - - VarDeclaration* v = fdecl->vthis; - if (v) { - // We already build the this argument here if we will need it - // later for codegen'ing the function, just as normal - // parameters below, because it can be referred to in nested - // context types. Will be given storage in DtoDefineFunction. - assert(!isIrParameterCreated(v)); - IrParameter *irParam = getIrParameter(v, true); - irParam->value = iarg; - irParam->arg = irFty.arg_this; - irParam->isVthis = true; - } - - ++iarg; - } - else if (irFty.arg_nest) { - iarg->setName(".nest_arg"); - irFunc->nestArg = iarg; - assert(irFunc->nestArg); - ++iarg; - } - - if (passThisBeforeSret) { - iarg->setName(".sret_arg"); - irFunc->retArg = iarg; - ++iarg; - } - - if (irFty.arg_arguments) { - iarg->setName("._arguments"); - irFunc->_arguments = iarg; - ++iarg; - } - - unsigned int k = 0; - for (; iarg != func->arg_end(); ++iarg) - { - size_t llExplicitIdx = irFty.reverseParams ? irFty.args.size() - k - 1 : k; - ++k; - IrFuncTyArg *arg = irFty.args[llExplicitIdx]; - - if (!fdecl->parameters || arg->parametersIdx >= fdecl->parameters->dim) - { - iarg->setName("unnamed"); - continue; - } - - Dsymbol* const argsym = (*fdecl->parameters)[arg->parametersIdx]; - VarDeclaration* argvd = argsym->isVarDeclaration(); - assert(argvd); - - iarg->setName(argvd->ident->toChars() + llvm::Twine("_arg")); - - IrParameter *irParam = getIrParameter(argvd, true); - irParam->arg = arg; - irParam->value = iarg; - } + IrParameter *irParam = getIrParameter(argvd, true); + irParam->arg = arg; + irParam->value = iarg; + } } ////////////////////////////////////////////////////////////////////////////////////////// -static LinkageWithCOMDAT lowerFuncLinkage(FuncDeclaration* fdecl) -{ - // Intrinsics are always external. - if (DtoIsIntrinsic(fdecl)) - return LinkageWithCOMDAT(llvm::GlobalValue::ExternalLinkage, false); +static LinkageWithCOMDAT lowerFuncLinkage(FuncDeclaration *fdecl) { + // Intrinsics are always external. + if (DtoIsIntrinsic(fdecl)) + return LinkageWithCOMDAT(llvm::GlobalValue::ExternalLinkage, false); - // Generated array op functions behave like templates in that they might be - // emitted into many different modules. - if (fdecl->isArrayOp && (willInline() || !isDruntimeArrayOp(fdecl))) - return LinkageWithCOMDAT(templateLinkage, supportsCOMDAT()); + // Generated array op functions behave like templates in that they might be + // emitted into many different modules. + if (fdecl->isArrayOp && (willInline() || !isDruntimeArrayOp(fdecl))) + return LinkageWithCOMDAT(templateLinkage, supportsCOMDAT()); - // A body-less declaration always needs to be marked as external in LLVM - // (also e.g. naked template functions which would otherwise be weak_odr, - // but where the definition is in module-level inline asm). - if (!fdecl->fbody || fdecl->naked) - return LinkageWithCOMDAT(llvm::GlobalValue::ExternalLinkage, false); + // A body-less declaration always needs to be marked as external in LLVM + // (also e.g. naked template functions which would otherwise be weak_odr, + // but where the definition is in module-level inline asm). + if (!fdecl->fbody || fdecl->naked) + return LinkageWithCOMDAT(llvm::GlobalValue::ExternalLinkage, false); - return DtoLinkage(fdecl); + return DtoLinkage(fdecl); } -void DtoDefineFunction(FuncDeclaration* fd) -{ - IF_LOG Logger::println("DtoDefineFunction(%s): %s", fd->toPrettyChars(), fd->loc.toChars()); - LOG_SCOPE; +void DtoDefineFunction(FuncDeclaration *fd) { + IF_LOG Logger::println("DtoDefineFunction(%s): %s", fd->toPrettyChars(), + fd->loc.toChars()); + LOG_SCOPE; - if (fd->ir.isDefined()) return; + if (fd->ir.isDefined()) + return; - if ((fd->type && fd->type->ty == Terror) || - (fd->type && fd->type->ty == Tfunction && static_cast(fd->type)->next == NULL) || - (fd->type && fd->type->ty == Tfunction && static_cast(fd->type)->next->ty == Terror)) - { - IF_LOG Logger::println("Ignoring; has error type, no return type or returns error type"); - fd->ir.setDefined(); - return; - } - - if (fd->semanticRun == PASSsemanticdone) - { - /* What happened is this function failed semantic3() with errors, - * but the errors were gagged. - * Try to reproduce those errors, and then fail. - */ - error(fd->loc, "errors compiling function %s", fd->toPrettyChars()); - fd->ir.setDefined(); - return; - } - - DtoResolveFunction(fd); - - if (fd->isUnitTestDeclaration() && !global.params.useUnitTests) - { - IF_LOG Logger::println("No code generation for unit test declaration %s", fd->toChars()); - fd->ir.setDefined(); - return; - } - - // Skip array ops implemented in druntime - if (fd->isArrayOp && !willInline() && isDruntimeArrayOp(fd)) - { - IF_LOG Logger::println("No code generation for array op %s implemented in druntime", fd->toChars()); - fd->ir.setDefined(); - return; - } - - // Check whether the frontend knows that the function is already defined - // in some other module (see DMD's FuncDeclaration::toObjFile). - for (FuncDeclaration *f = fd; f; ) - { - if (!f->isInstantiated() && f->inNonRoot()) - { - IF_LOG Logger::println("Skipping '%s'.", fd->toPrettyChars()); - // TODO: Emit as available_externally for inlining purposes instead - // (see #673). - fd->ir.setDefined(); - return; - } - if (f->isNested()) - f = f->toParent2()->isFuncDeclaration(); - else - break; - } - - DtoDeclareFunction(fd); - assert(fd->ir.isDeclared()); - - // DtoResolveFunction might also set the defined flag for functions we - // should not touch. - if (fd->ir.isDefined()) return; + if ((fd->type && fd->type->ty == Terror) || + (fd->type && fd->type->ty == Tfunction && + static_cast(fd->type)->next == NULL) || + (fd->type && fd->type->ty == Tfunction && + static_cast(fd->type)->next->ty == Terror)) { + IF_LOG Logger::println( + "Ignoring; has error type, no return type or returns error type"); fd->ir.setDefined(); + return; + } - // We cannot emit nested functions with parents that have not gone through - // semantic analysis. This can happen as DMD leaks some template instances - // from constraints into the module member list. DMD gets away with being - // sloppy as functions in template contraints obviously never need to access - // data from the template function itself, but it would still mess up our - // nested context creation code. - FuncDeclaration* parent = fd; - while ((parent = getParentFunc(parent, true))) - { - if (parent->semanticRun != PASSsemantic3done || parent->semantic3Errors) - { - IF_LOG Logger::println("Ignoring nested function with unanalyzed parent."); - return; - } - } - - assert(fd->semanticRun == PASSsemantic3done); - assert(fd->ident != Id::empty); - - if (fd->isUnitTestDeclaration()) { - getIrModule(gIR->dmodule)->unitTests.push_back(fd); - } else if (fd->isSharedStaticCtorDeclaration()) { - getIrModule(gIR->dmodule)->sharedCtors.push_back(fd); - } else if (StaticDtorDeclaration *dtorDecl = fd->isSharedStaticDtorDeclaration()) { - getIrModule(gIR->dmodule)->sharedDtors.push_front(fd); - if (dtorDecl->vgate) { - getIrModule(gIR->dmodule)->sharedGates.push_front(dtorDecl->vgate); - } - } else if (fd->isStaticCtorDeclaration()) { - getIrModule(gIR->dmodule)->ctors.push_back(fd); - } else if (StaticDtorDeclaration *dtorDecl = fd->isStaticDtorDeclaration()) { - getIrModule(gIR->dmodule)->dtors.push_front(fd); - if (dtorDecl->vgate) { - getIrModule(gIR->dmodule)->gates.push_front(dtorDecl->vgate); - } - } - - - // if this function is naked, we take over right away! no standard processing! - if (fd->naked) - { - DtoDefineNakedFunction(fd); - return; - } - - IrFunction *irFunc = getIrFunc(fd); - IrFuncTy &irFty = irFunc->irFty; - - // debug info - irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd); - - Type* t = fd->type->toBasetype(); - TypeFunction* f = static_cast(t); - // assert(f->ctype); - - llvm::Function* func = irFunc->func; - - // is there a body? - if (fd->fbody == NULL) - return; - - IF_LOG Logger::println("Doing function body for: %s", fd->toChars()); - gIR->functions.push_back(irFunc); - - LinkageWithCOMDAT lwc = lowerFuncLinkage(fd); - func->setLinkage(lwc.first); - if (lwc.second) SET_COMDAT(func, gIR->module); - - // On x86_64, always set 'uwtable' for System V ABI compatibility. - // TODO: Find a better place for this. - // TODO: Is this required for Win64 as well? - if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) - { - func->addFnAttr(LLAttribute::UWTable); - } - if (opts::sanitize != opts::None) { - // Set the required sanitizer attribute. - if (opts::sanitize == opts::AddressSanitizer) { - func->addFnAttr(LLAttribute::SanitizeAddress); - } - - if (opts::sanitize == opts::MemorySanitizer) { - func->addFnAttr(LLAttribute::SanitizeMemory); - } - - if (opts::sanitize == opts::ThreadSanitizer) { - func->addFnAttr(LLAttribute::SanitizeThread); - } - } - - llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(gIR->context(), "", func); - - //assert(gIR->scopes.empty()); - gIR->scopes.push_back(IRScope(beginbb)); - - // create alloca point - // this gets erased when the function is complete, so alignment etc does not matter at all - llvm::Instruction* allocaPoint = new llvm::AllocaInst(LLType::getInt32Ty(gIR->context()), "alloca point", beginbb); - irFunc->allocapoint = allocaPoint; - - // debug info - after all allocas, but before any llvm.dbg.declare etc - gIR->DBuilder.EmitFuncStart(fd); - - // this hack makes sure the frame pointer elimination optimization is disabled. - // this this eliminates a bunch of inline asm related issues. - if (fd->hasReturnExp & 8) // has inline asm - { - // emit a call to llvm_eh_unwind_init - LLFunction* hack = GET_INTRINSIC_DECL(eh_unwind_init); -#if LDC_LLVM_VER >= 307 - gIR->ir->CreateCall(hack, {}); -#else - gIR->ir->CreateCall(hack, ""); -#endif - } - - // give the 'this' argument storage and debug info - if (irFty.arg_this) - { - LLValue* thisvar = irFunc->thisArg; - assert(thisvar); - - LLValue* thismem = thisvar; - if (!irFty.arg_this->byref) - { - thismem = DtoAllocaDump(thisvar, 0, "this"); - irFunc->thisArg = thismem; - } - - assert(getIrParameter(fd->vthis)->value == thisvar); - getIrParameter(fd->vthis)->value = thismem; - - gIR->DBuilder.EmitLocalVariable(thismem, fd->vthis, 0, true); - } - - // give the 'nestArg' storage - if (irFty.arg_nest) - irFunc->nestArg = DtoAllocaDump(irFunc->nestArg, 0, "nestedFrame"); - - // give arguments storage and debug info - if (fd->parameters) - { - // Not all arguments are necessarily passed on the LLVM level - // (e.g. zero-member structs), so we need to keep track of the - // index in the IrFuncTy args array separately. - size_t llArgIdx = 0; - for (size_t i = 0; i < fd->parameters->dim; ++i) - { - Dsymbol* const argsym = (*fd->parameters)[i]; - VarDeclaration* const vd = argsym->isVarDeclaration(); - assert(vd); - const bool refout = vd->storage_class & (STCref | STCout); - - IrParameter* irparam = getIrParameter(vd); - Type* debugInfoType = vd->type; - if (!irparam) - { - // This is a parameter that is not passed on the LLVM level. - // Create the param here and set it to a "dummy" alloca that - // we do not store to here. - irparam = getIrParameter(vd, true); - irparam->value = DtoAlloca(vd, vd->ident->toChars()); - } - else - { - const bool lazy = vd->storage_class & STClazy; - const bool firstClassVal = !refout && (!irparam->arg->byref || lazy); - if (firstClassVal) - { - // alloca a stack slot for this first class value arg - LLValue* mem = DtoAlloca(irparam->arg->type, vd->ident->toChars()); - - // let the abi transform the argument back first - irFty.getParam(vd->type, llArgIdx, irparam->value, mem); - - // set the arg var value to the alloca - irparam->value = mem; - - debugInfoType = irparam->arg->type; - } - ++llArgIdx; - } - - if (global.params.symdebug && !(isaArgument(irparam->value) && isaArgument(irparam->value)->hasByValAttr())) - gIR->DBuilder.EmitLocalVariable(irparam->value, vd, debugInfoType); - } - } - - { - ScopeStack scopeStack(gIR); - irFunc->scopes = &scopeStack; - - DtoCreateNestedContext(fd); - - if (fd->vresult && !fd->vresult->nestedrefs.dim) // FIXME: not sure here :/ - { - DtoVarDeclaration(fd->vresult); - } - - // D varargs: prepare _argptr and _arguments - if (f->linkage == LINKd && f->varargs == 1) - { - // allocate _argptr (of type core.stdc.stdarg.va_list) - LLValue* argptrmem = DtoAlloca(Type::tvalist, "_argptr_mem"); - irFunc->_argptr = argptrmem; - - // initialize _argptr with a call to the va_start intrinsic - LLValue* vaStartArg = gABI->prepareVaStart(argptrmem); - llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "", gIR->scopebb()); - - // copy _arguments to a memory location - irFunc->_arguments = DtoAllocaDump(irFunc->_arguments, 0, "_arguments_mem"); - } - - // output function body - Statement_toIR(fd->fbody, gIR); - - irFunc->scopes = 0; - } - - llvm::BasicBlock* bb = gIR->scopebb(); - if (pred_begin(bb) == pred_end(bb) && bb != &bb->getParent()->getEntryBlock()) { - // This block is trivially unreachable, so just delete it. - // (This is a common case because it happens when 'return' - // is the last statement in a function) - bb->eraseFromParent(); - } else if (!gIR->scopereturned()) { - // llvm requires all basic blocks to end with a TerminatorInst but DMD does not put a return statement - // in automatically, so we do it here. - - // pass the previous block into this block - gIR->DBuilder.EmitStopPoint(fd->endloc); - if (func->getReturnType() == LLType::getVoidTy(gIR->context())) { - gIR->ir->CreateRetVoid(); - } - else if (!fd->isMain()) { - CompoundAsmStatement* asmb = fd->fbody->endsWithAsm(); - if (asmb) { - assert(asmb->abiret); - gIR->ir->CreateRet(asmb->abiret); - } - else { - gIR->ir->CreateRet(llvm::UndefValue::get(func->getReturnType())); - } - } - else - gIR->ir->CreateRet(LLConstant::getNullValue(func->getReturnType())); - } - gIR->DBuilder.EmitFuncEnd(fd); - - // erase alloca point - if (allocaPoint->getParent()) - allocaPoint->eraseFromParent(); - allocaPoint = 0; - gIR->func()->allocapoint = 0; - - gIR->scopes.pop_back(); - - gIR->functions.pop_back(); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -DValue* DtoArgument(Parameter* fnarg, Expression* argexp) -{ - IF_LOG Logger::println("DtoArgument"); - LOG_SCOPE; - - // ref/out arg - if (fnarg && (fnarg->storageClass & (STCref | STCout))) - { - Loc loc; - DValue* arg = toElem(argexp, true); - return new DImValue(argexp->type, arg->isLVal() ? arg->getLVal() : makeLValue(loc, arg)); - } - - DValue* arg = toElem(argexp); - - // lazy arg - if (fnarg && (fnarg->storageClass & STClazy)) - { - assert(argexp->type->toBasetype()->ty == Tdelegate); - assert(!arg->isLVal()); - return arg; - } - - // byval arg, but expr has no storage yet - if (DtoIsPassedByRef(argexp->type) && (arg->isSlice() || arg->isNull())) - { - LLValue* alloc = DtoAlloca(argexp->type, ".tmp_arg"); - DVarValue* vv = new DVarValue(argexp->type, alloc); - DtoAssign(argexp->loc, vv, arg); - arg = vv; - } - - return arg; -} - -////////////////////////////////////////////////////////////////////////////////////////// - -int binary(const char *p , const char **tab, int high) -{ - int i = 0, j = high, k, l; - do - { - k = (i + j) / 2; - l = strcmp(p, tab[k]); - if (!l) - return k; - else if (l < 0) - j = k; - else - i = k + 1; - } - while (i != j); - return -1; -} - -int isDruntimeArrayOp(FuncDeclaration *fd) -{ - /* Some of the array op functions are written as library functions, - * presumably to optimize them with special CPU vector instructions. - * List those library functions here, in alpha order. + if (fd->semanticRun == PASSsemanticdone) { + /* What happened is this function failed semantic3() with errors, + * but the errors were gagged. + * Try to reproduce those errors, and then fail. */ - static const char *libArrayopFuncs[] = - { - "_arrayExpSliceAddass_a", - "_arrayExpSliceAddass_d", - "_arrayExpSliceAddass_f", // T[]+=T - "_arrayExpSliceAddass_g", - "_arrayExpSliceAddass_h", - "_arrayExpSliceAddass_i", - "_arrayExpSliceAddass_k", - "_arrayExpSliceAddass_s", - "_arrayExpSliceAddass_t", - "_arrayExpSliceAddass_u", - "_arrayExpSliceAddass_w", + error(fd->loc, "errors compiling function %s", fd->toPrettyChars()); + fd->ir.setDefined(); + return; + } - "_arrayExpSliceDivass_d", // T[]/=T - "_arrayExpSliceDivass_f", // T[]/=T + DtoResolveFunction(fd); - "_arrayExpSliceMinSliceAssign_a", - "_arrayExpSliceMinSliceAssign_d", // T[]=T-T[] - "_arrayExpSliceMinSliceAssign_f", // T[]=T-T[] - "_arrayExpSliceMinSliceAssign_g", - "_arrayExpSliceMinSliceAssign_h", - "_arrayExpSliceMinSliceAssign_i", - "_arrayExpSliceMinSliceAssign_k", - "_arrayExpSliceMinSliceAssign_s", - "_arrayExpSliceMinSliceAssign_t", - "_arrayExpSliceMinSliceAssign_u", - "_arrayExpSliceMinSliceAssign_w", + if (fd->isUnitTestDeclaration() && !global.params.useUnitTests) { + IF_LOG Logger::println("No code generation for unit test declaration %s", + fd->toChars()); + fd->ir.setDefined(); + return; + } - "_arrayExpSliceMinass_a", - "_arrayExpSliceMinass_d", // T[]-=T - "_arrayExpSliceMinass_f", // T[]-=T - "_arrayExpSliceMinass_g", - "_arrayExpSliceMinass_h", - "_arrayExpSliceMinass_i", - "_arrayExpSliceMinass_k", - "_arrayExpSliceMinass_s", - "_arrayExpSliceMinass_t", - "_arrayExpSliceMinass_u", - "_arrayExpSliceMinass_w", + // Skip array ops implemented in druntime + if (fd->isArrayOp && !willInline() && isDruntimeArrayOp(fd)) { + IF_LOG Logger::println( + "No code generation for array op %s implemented in druntime", + fd->toChars()); + fd->ir.setDefined(); + return; + } - "_arrayExpSliceMulass_d", // T[]*=T - "_arrayExpSliceMulass_f", // T[]*=T - "_arrayExpSliceMulass_i", - "_arrayExpSliceMulass_k", - "_arrayExpSliceMulass_s", - "_arrayExpSliceMulass_t", - "_arrayExpSliceMulass_u", - "_arrayExpSliceMulass_w", - - "_arraySliceExpAddSliceAssign_a", - "_arraySliceExpAddSliceAssign_d", // T[]=T[]+T - "_arraySliceExpAddSliceAssign_f", // T[]=T[]+T - "_arraySliceExpAddSliceAssign_g", - "_arraySliceExpAddSliceAssign_h", - "_arraySliceExpAddSliceAssign_i", - "_arraySliceExpAddSliceAssign_k", - "_arraySliceExpAddSliceAssign_s", - "_arraySliceExpAddSliceAssign_t", - "_arraySliceExpAddSliceAssign_u", - "_arraySliceExpAddSliceAssign_w", - - "_arraySliceExpDivSliceAssign_d", // T[]=T[]/T - "_arraySliceExpDivSliceAssign_f", // T[]=T[]/T - - "_arraySliceExpMinSliceAssign_a", - "_arraySliceExpMinSliceAssign_d", // T[]=T[]-T - "_arraySliceExpMinSliceAssign_f", // T[]=T[]-T - "_arraySliceExpMinSliceAssign_g", - "_arraySliceExpMinSliceAssign_h", - "_arraySliceExpMinSliceAssign_i", - "_arraySliceExpMinSliceAssign_k", - "_arraySliceExpMinSliceAssign_s", - "_arraySliceExpMinSliceAssign_t", - "_arraySliceExpMinSliceAssign_u", - "_arraySliceExpMinSliceAssign_w", - - "_arraySliceExpMulSliceAddass_d", // T[] += T[]*T - "_arraySliceExpMulSliceAddass_f", - "_arraySliceExpMulSliceAddass_r", - - "_arraySliceExpMulSliceAssign_d", // T[]=T[]*T - "_arraySliceExpMulSliceAssign_f", // T[]=T[]*T - "_arraySliceExpMulSliceAssign_i", - "_arraySliceExpMulSliceAssign_k", - "_arraySliceExpMulSliceAssign_s", - "_arraySliceExpMulSliceAssign_t", - "_arraySliceExpMulSliceAssign_u", - "_arraySliceExpMulSliceAssign_w", - - "_arraySliceExpMulSliceMinass_d", // T[] -= T[]*T - "_arraySliceExpMulSliceMinass_f", - "_arraySliceExpMulSliceMinass_r", - - "_arraySliceSliceAddSliceAssign_a", - "_arraySliceSliceAddSliceAssign_d", // T[]=T[]+T[] - "_arraySliceSliceAddSliceAssign_f", // T[]=T[]+T[] - "_arraySliceSliceAddSliceAssign_g", - "_arraySliceSliceAddSliceAssign_h", - "_arraySliceSliceAddSliceAssign_i", - "_arraySliceSliceAddSliceAssign_k", - "_arraySliceSliceAddSliceAssign_r", // T[]=T[]+T[] - "_arraySliceSliceAddSliceAssign_s", - "_arraySliceSliceAddSliceAssign_t", - "_arraySliceSliceAddSliceAssign_u", - "_arraySliceSliceAddSliceAssign_w", - - "_arraySliceSliceAddass_a", - "_arraySliceSliceAddass_d", // T[]+=T[] - "_arraySliceSliceAddass_f", // T[]+=T[] - "_arraySliceSliceAddass_g", - "_arraySliceSliceAddass_h", - "_arraySliceSliceAddass_i", - "_arraySliceSliceAddass_k", - "_arraySliceSliceAddass_s", - "_arraySliceSliceAddass_t", - "_arraySliceSliceAddass_u", - "_arraySliceSliceAddass_w", - - "_arraySliceSliceMinSliceAssign_a", - "_arraySliceSliceMinSliceAssign_d", // T[]=T[]-T[] - "_arraySliceSliceMinSliceAssign_f", // T[]=T[]-T[] - "_arraySliceSliceMinSliceAssign_g", - "_arraySliceSliceMinSliceAssign_h", - "_arraySliceSliceMinSliceAssign_i", - "_arraySliceSliceMinSliceAssign_k", - "_arraySliceSliceMinSliceAssign_r", // T[]=T[]-T[] - "_arraySliceSliceMinSliceAssign_s", - "_arraySliceSliceMinSliceAssign_t", - "_arraySliceSliceMinSliceAssign_u", - "_arraySliceSliceMinSliceAssign_w", - - "_arraySliceSliceMinass_a", - "_arraySliceSliceMinass_d", // T[]-=T[] - "_arraySliceSliceMinass_f", // T[]-=T[] - "_arraySliceSliceMinass_g", - "_arraySliceSliceMinass_h", - "_arraySliceSliceMinass_i", - "_arraySliceSliceMinass_k", - "_arraySliceSliceMinass_s", - "_arraySliceSliceMinass_t", - "_arraySliceSliceMinass_u", - "_arraySliceSliceMinass_w", - - "_arraySliceSliceMulSliceAssign_d", // T[]=T[]*T[] - "_arraySliceSliceMulSliceAssign_f", // T[]=T[]*T[] - "_arraySliceSliceMulSliceAssign_i", - "_arraySliceSliceMulSliceAssign_k", - "_arraySliceSliceMulSliceAssign_s", - "_arraySliceSliceMulSliceAssign_t", - "_arraySliceSliceMulSliceAssign_u", - "_arraySliceSliceMulSliceAssign_w", - - "_arraySliceSliceMulass_d", // T[]*=T[] - "_arraySliceSliceMulass_f", // T[]*=T[] - "_arraySliceSliceMulass_i", - "_arraySliceSliceMulass_k", - "_arraySliceSliceMulass_s", - "_arraySliceSliceMulass_t", - "_arraySliceSliceMulass_u", - "_arraySliceSliceMulass_w", - }; - char *name = fd->ident->toChars(); - int i = binary(name, libArrayopFuncs, sizeof(libArrayopFuncs) / sizeof(char *)); - if (i != -1) - return 1; - -#ifdef DEBUG // Make sure our array is alphabetized - for (i = 0; i < sizeof(libArrayopFuncs) / sizeof(char *); i++) - { - if (strcmp(name, libArrayopFuncs[i]) == 0) - assert(0); + // Check whether the frontend knows that the function is already defined + // in some other module (see DMD's FuncDeclaration::toObjFile). + for (FuncDeclaration *f = fd; f;) { + if (!f->isInstantiated() && f->inNonRoot()) { + IF_LOG Logger::println("Skipping '%s'.", fd->toPrettyChars()); + // TODO: Emit as available_externally for inlining purposes instead + // (see #673). + fd->ir.setDefined(); + return; } + if (f->isNested()) + f = f->toParent2()->isFuncDeclaration(); + else + break; + } + + DtoDeclareFunction(fd); + assert(fd->ir.isDeclared()); + + // DtoResolveFunction might also set the defined flag for functions we + // should not touch. + if (fd->ir.isDefined()) + return; + fd->ir.setDefined(); + + // We cannot emit nested functions with parents that have not gone through + // semantic analysis. This can happen as DMD leaks some template instances + // from constraints into the module member list. DMD gets away with being + // sloppy as functions in template contraints obviously never need to access + // data from the template function itself, but it would still mess up our + // nested context creation code. + FuncDeclaration *parent = fd; + while ((parent = getParentFunc(parent, true))) { + if (parent->semanticRun != PASSsemantic3done || parent->semantic3Errors) { + IF_LOG Logger::println( + "Ignoring nested function with unanalyzed parent."); + return; + } + } + + assert(fd->semanticRun == PASSsemantic3done); + assert(fd->ident != Id::empty); + + if (fd->isUnitTestDeclaration()) { + getIrModule(gIR->dmodule)->unitTests.push_back(fd); + } else if (fd->isSharedStaticCtorDeclaration()) { + getIrModule(gIR->dmodule)->sharedCtors.push_back(fd); + } else if (StaticDtorDeclaration *dtorDecl = + fd->isSharedStaticDtorDeclaration()) { + getIrModule(gIR->dmodule)->sharedDtors.push_front(fd); + if (dtorDecl->vgate) { + getIrModule(gIR->dmodule)->sharedGates.push_front(dtorDecl->vgate); + } + } else if (fd->isStaticCtorDeclaration()) { + getIrModule(gIR->dmodule)->ctors.push_back(fd); + } else if (StaticDtorDeclaration *dtorDecl = fd->isStaticDtorDeclaration()) { + getIrModule(gIR->dmodule)->dtors.push_front(fd); + if (dtorDecl->vgate) { + getIrModule(gIR->dmodule)->gates.push_front(dtorDecl->vgate); + } + } + + // if this function is naked, we take over right away! no standard processing! + if (fd->naked) { + DtoDefineNakedFunction(fd); + return; + } + + IrFunction *irFunc = getIrFunc(fd); + IrFuncTy &irFty = irFunc->irFty; + + // debug info + irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd); + + Type *t = fd->type->toBasetype(); + TypeFunction *f = static_cast(t); + // assert(f->ctype); + + llvm::Function *func = irFunc->func; + + // is there a body? + if (fd->fbody == NULL) + return; + + IF_LOG Logger::println("Doing function body for: %s", fd->toChars()); + gIR->functions.push_back(irFunc); + + LinkageWithCOMDAT lwc = lowerFuncLinkage(fd); + func->setLinkage(lwc.first); + if (lwc.second) + SET_COMDAT(func, gIR->module); + + // On x86_64, always set 'uwtable' for System V ABI compatibility. + // TODO: Find a better place for this. + // TODO: Is this required for Win64 as well? + if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) { + func->addFnAttr(LLAttribute::UWTable); + } + if (opts::sanitize != opts::None) { + // Set the required sanitizer attribute. + if (opts::sanitize == opts::AddressSanitizer) { + func->addFnAttr(LLAttribute::SanitizeAddress); + } + + if (opts::sanitize == opts::MemorySanitizer) { + func->addFnAttr(LLAttribute::SanitizeMemory); + } + + if (opts::sanitize == opts::ThreadSanitizer) { + func->addFnAttr(LLAttribute::SanitizeThread); + } + } + + llvm::BasicBlock *beginbb = + llvm::BasicBlock::Create(gIR->context(), "", func); + + // assert(gIR->scopes.empty()); + gIR->scopes.push_back(IRScope(beginbb)); + + // create alloca point + // this gets erased when the function is complete, so alignment etc does not + // matter at all + llvm::Instruction *allocaPoint = new llvm::AllocaInst( + LLType::getInt32Ty(gIR->context()), "alloca point", beginbb); + irFunc->allocapoint = allocaPoint; + + // debug info - after all allocas, but before any llvm.dbg.declare etc + gIR->DBuilder.EmitFuncStart(fd); + + // this hack makes sure the frame pointer elimination optimization is + // disabled. + // this this eliminates a bunch of inline asm related issues. + if (fd->hasReturnExp & 8) // has inline asm + { + // emit a call to llvm_eh_unwind_init + LLFunction *hack = GET_INTRINSIC_DECL(eh_unwind_init); +#if LDC_LLVM_VER >= 307 + gIR->ir->CreateCall(hack, {}); +#else + gIR->ir->CreateCall(hack, ""); #endif - return 0; + } + + // give the 'this' argument storage and debug info + if (irFty.arg_this) { + LLValue *thisvar = irFunc->thisArg; + assert(thisvar); + + LLValue *thismem = thisvar; + if (!irFty.arg_this->byref) { + thismem = DtoAllocaDump(thisvar, 0, "this"); + irFunc->thisArg = thismem; + } + + assert(getIrParameter(fd->vthis)->value == thisvar); + getIrParameter(fd->vthis)->value = thismem; + + gIR->DBuilder.EmitLocalVariable(thismem, fd->vthis, 0, true); + } + + // give the 'nestArg' storage + if (irFty.arg_nest) + irFunc->nestArg = DtoAllocaDump(irFunc->nestArg, 0, "nestedFrame"); + + // give arguments storage and debug info + if (fd->parameters) { + // Not all arguments are necessarily passed on the LLVM level + // (e.g. zero-member structs), so we need to keep track of the + // index in the IrFuncTy args array separately. + size_t llArgIdx = 0; + for (size_t i = 0; i < fd->parameters->dim; ++i) { + Dsymbol *const argsym = (*fd->parameters)[i]; + VarDeclaration *const vd = argsym->isVarDeclaration(); + assert(vd); + const bool refout = vd->storage_class & (STCref | STCout); + + IrParameter *irparam = getIrParameter(vd); + Type *debugInfoType = vd->type; + if (!irparam) { + // This is a parameter that is not passed on the LLVM level. + // Create the param here and set it to a "dummy" alloca that + // we do not store to here. + irparam = getIrParameter(vd, true); + irparam->value = DtoAlloca(vd, vd->ident->toChars()); + } else { + const bool lazy = vd->storage_class & STClazy; + const bool firstClassVal = !refout && (!irparam->arg->byref || lazy); + if (firstClassVal) { + // alloca a stack slot for this first class value arg + LLValue *mem = DtoAlloca(irparam->arg->type, vd->ident->toChars()); + + // let the abi transform the argument back first + irFty.getParam(vd->type, llArgIdx, irparam->value, mem); + + // set the arg var value to the alloca + irparam->value = mem; + + debugInfoType = irparam->arg->type; + } + ++llArgIdx; + } + + if (global.params.symdebug && + !(isaArgument(irparam->value) && + isaArgument(irparam->value)->hasByValAttr())) + gIR->DBuilder.EmitLocalVariable(irparam->value, vd, debugInfoType); + } + } + + { + ScopeStack scopeStack(gIR); + irFunc->scopes = &scopeStack; + + DtoCreateNestedContext(fd); + + if (fd->vresult && !fd->vresult->nestedrefs.dim) // FIXME: not sure here :/ + { + DtoVarDeclaration(fd->vresult); + } + + // D varargs: prepare _argptr and _arguments + if (f->linkage == LINKd && f->varargs == 1) { + // allocate _argptr (of type core.stdc.stdarg.va_list) + LLValue *argptrmem = DtoAlloca(Type::tvalist, "_argptr_mem"); + irFunc->_argptr = argptrmem; + + // initialize _argptr with a call to the va_start intrinsic + LLValue *vaStartArg = gABI->prepareVaStart(argptrmem); + llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "", + gIR->scopebb()); + + // copy _arguments to a memory location + irFunc->_arguments = + DtoAllocaDump(irFunc->_arguments, 0, "_arguments_mem"); + } + + // output function body + Statement_toIR(fd->fbody, gIR); + + irFunc->scopes = 0; + } + + llvm::BasicBlock *bb = gIR->scopebb(); + if (pred_begin(bb) == pred_end(bb) && + bb != &bb->getParent()->getEntryBlock()) { + // This block is trivially unreachable, so just delete it. + // (This is a common case because it happens when 'return' + // is the last statement in a function) + bb->eraseFromParent(); + } else if (!gIR->scopereturned()) { + // llvm requires all basic blocks to end with a TerminatorInst but DMD does + // not put a return statement + // in automatically, so we do it here. + + // pass the previous block into this block + gIR->DBuilder.EmitStopPoint(fd->endloc); + if (func->getReturnType() == LLType::getVoidTy(gIR->context())) { + gIR->ir->CreateRetVoid(); + } else if (!fd->isMain()) { + CompoundAsmStatement *asmb = fd->fbody->endsWithAsm(); + if (asmb) { + assert(asmb->abiret); + gIR->ir->CreateRet(asmb->abiret); + } else { + gIR->ir->CreateRet(llvm::UndefValue::get(func->getReturnType())); + } + } else + gIR->ir->CreateRet(LLConstant::getNullValue(func->getReturnType())); + } + gIR->DBuilder.EmitFuncEnd(fd); + + // erase alloca point + if (allocaPoint->getParent()) + allocaPoint->eraseFromParent(); + allocaPoint = 0; + gIR->func()->allocapoint = 0; + + gIR->scopes.pop_back(); + + gIR->functions.pop_back(); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +DValue *DtoArgument(Parameter *fnarg, Expression *argexp) { + IF_LOG Logger::println("DtoArgument"); + LOG_SCOPE; + + // ref/out arg + if (fnarg && (fnarg->storageClass & (STCref | STCout))) { + Loc loc; + DValue *arg = toElem(argexp, true); + return new DImValue(argexp->type, + arg->isLVal() ? arg->getLVal() : makeLValue(loc, arg)); + } + + DValue *arg = toElem(argexp); + + // lazy arg + if (fnarg && (fnarg->storageClass & STClazy)) { + assert(argexp->type->toBasetype()->ty == Tdelegate); + assert(!arg->isLVal()); + return arg; + } + + // byval arg, but expr has no storage yet + if (DtoIsPassedByRef(argexp->type) && (arg->isSlice() || arg->isNull())) { + LLValue *alloc = DtoAlloca(argexp->type, ".tmp_arg"); + DVarValue *vv = new DVarValue(argexp->type, alloc); + DtoAssign(argexp->loc, vv, arg); + arg = vv; + } + + return arg; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +int binary(const char *p, const char **tab, int high) { + int i = 0, j = high, k, l; + do { + k = (i + j) / 2; + l = strcmp(p, tab[k]); + if (!l) + return k; + else if (l < 0) + j = k; + else + i = k + 1; + } while (i != j); + return -1; +} + +int isDruntimeArrayOp(FuncDeclaration *fd) { + /* Some of the array op functions are written as library functions, + * presumably to optimize them with special CPU vector instructions. + * List those library functions here, in alpha order. + */ + static const char *libArrayopFuncs[] = { + "_arrayExpSliceAddass_a", "_arrayExpSliceAddass_d", + "_arrayExpSliceAddass_f", // T[]+=T + "_arrayExpSliceAddass_g", "_arrayExpSliceAddass_h", + "_arrayExpSliceAddass_i", "_arrayExpSliceAddass_k", + "_arrayExpSliceAddass_s", "_arrayExpSliceAddass_t", + "_arrayExpSliceAddass_u", "_arrayExpSliceAddass_w", + + "_arrayExpSliceDivass_d", // T[]/=T + "_arrayExpSliceDivass_f", // T[]/=T + + "_arrayExpSliceMinSliceAssign_a", + "_arrayExpSliceMinSliceAssign_d", // T[]=T-T[] + "_arrayExpSliceMinSliceAssign_f", // T[]=T-T[] + "_arrayExpSliceMinSliceAssign_g", "_arrayExpSliceMinSliceAssign_h", + "_arrayExpSliceMinSliceAssign_i", "_arrayExpSliceMinSliceAssign_k", + "_arrayExpSliceMinSliceAssign_s", "_arrayExpSliceMinSliceAssign_t", + "_arrayExpSliceMinSliceAssign_u", "_arrayExpSliceMinSliceAssign_w", + + "_arrayExpSliceMinass_a", + "_arrayExpSliceMinass_d", // T[]-=T + "_arrayExpSliceMinass_f", // T[]-=T + "_arrayExpSliceMinass_g", "_arrayExpSliceMinass_h", + "_arrayExpSliceMinass_i", "_arrayExpSliceMinass_k", + "_arrayExpSliceMinass_s", "_arrayExpSliceMinass_t", + "_arrayExpSliceMinass_u", "_arrayExpSliceMinass_w", + + "_arrayExpSliceMulass_d", // T[]*=T + "_arrayExpSliceMulass_f", // T[]*=T + "_arrayExpSliceMulass_i", "_arrayExpSliceMulass_k", + "_arrayExpSliceMulass_s", "_arrayExpSliceMulass_t", + "_arrayExpSliceMulass_u", "_arrayExpSliceMulass_w", + + "_arraySliceExpAddSliceAssign_a", + "_arraySliceExpAddSliceAssign_d", // T[]=T[]+T + "_arraySliceExpAddSliceAssign_f", // T[]=T[]+T + "_arraySliceExpAddSliceAssign_g", "_arraySliceExpAddSliceAssign_h", + "_arraySliceExpAddSliceAssign_i", "_arraySliceExpAddSliceAssign_k", + "_arraySliceExpAddSliceAssign_s", "_arraySliceExpAddSliceAssign_t", + "_arraySliceExpAddSliceAssign_u", "_arraySliceExpAddSliceAssign_w", + + "_arraySliceExpDivSliceAssign_d", // T[]=T[]/T + "_arraySliceExpDivSliceAssign_f", // T[]=T[]/T + + "_arraySliceExpMinSliceAssign_a", + "_arraySliceExpMinSliceAssign_d", // T[]=T[]-T + "_arraySliceExpMinSliceAssign_f", // T[]=T[]-T + "_arraySliceExpMinSliceAssign_g", "_arraySliceExpMinSliceAssign_h", + "_arraySliceExpMinSliceAssign_i", "_arraySliceExpMinSliceAssign_k", + "_arraySliceExpMinSliceAssign_s", "_arraySliceExpMinSliceAssign_t", + "_arraySliceExpMinSliceAssign_u", "_arraySliceExpMinSliceAssign_w", + + "_arraySliceExpMulSliceAddass_d", // T[] += T[]*T + "_arraySliceExpMulSliceAddass_f", "_arraySliceExpMulSliceAddass_r", + + "_arraySliceExpMulSliceAssign_d", // T[]=T[]*T + "_arraySliceExpMulSliceAssign_f", // T[]=T[]*T + "_arraySliceExpMulSliceAssign_i", "_arraySliceExpMulSliceAssign_k", + "_arraySliceExpMulSliceAssign_s", "_arraySliceExpMulSliceAssign_t", + "_arraySliceExpMulSliceAssign_u", "_arraySliceExpMulSliceAssign_w", + + "_arraySliceExpMulSliceMinass_d", // T[] -= T[]*T + "_arraySliceExpMulSliceMinass_f", "_arraySliceExpMulSliceMinass_r", + + "_arraySliceSliceAddSliceAssign_a", + "_arraySliceSliceAddSliceAssign_d", // T[]=T[]+T[] + "_arraySliceSliceAddSliceAssign_f", // T[]=T[]+T[] + "_arraySliceSliceAddSliceAssign_g", "_arraySliceSliceAddSliceAssign_h", + "_arraySliceSliceAddSliceAssign_i", "_arraySliceSliceAddSliceAssign_k", + "_arraySliceSliceAddSliceAssign_r", // T[]=T[]+T[] + "_arraySliceSliceAddSliceAssign_s", "_arraySliceSliceAddSliceAssign_t", + "_arraySliceSliceAddSliceAssign_u", "_arraySliceSliceAddSliceAssign_w", + + "_arraySliceSliceAddass_a", + "_arraySliceSliceAddass_d", // T[]+=T[] + "_arraySliceSliceAddass_f", // T[]+=T[] + "_arraySliceSliceAddass_g", "_arraySliceSliceAddass_h", + "_arraySliceSliceAddass_i", "_arraySliceSliceAddass_k", + "_arraySliceSliceAddass_s", "_arraySliceSliceAddass_t", + "_arraySliceSliceAddass_u", "_arraySliceSliceAddass_w", + + "_arraySliceSliceMinSliceAssign_a", + "_arraySliceSliceMinSliceAssign_d", // T[]=T[]-T[] + "_arraySliceSliceMinSliceAssign_f", // T[]=T[]-T[] + "_arraySliceSliceMinSliceAssign_g", "_arraySliceSliceMinSliceAssign_h", + "_arraySliceSliceMinSliceAssign_i", "_arraySliceSliceMinSliceAssign_k", + "_arraySliceSliceMinSliceAssign_r", // T[]=T[]-T[] + "_arraySliceSliceMinSliceAssign_s", "_arraySliceSliceMinSliceAssign_t", + "_arraySliceSliceMinSliceAssign_u", "_arraySliceSliceMinSliceAssign_w", + + "_arraySliceSliceMinass_a", + "_arraySliceSliceMinass_d", // T[]-=T[] + "_arraySliceSliceMinass_f", // T[]-=T[] + "_arraySliceSliceMinass_g", "_arraySliceSliceMinass_h", + "_arraySliceSliceMinass_i", "_arraySliceSliceMinass_k", + "_arraySliceSliceMinass_s", "_arraySliceSliceMinass_t", + "_arraySliceSliceMinass_u", "_arraySliceSliceMinass_w", + + "_arraySliceSliceMulSliceAssign_d", // T[]=T[]*T[] + "_arraySliceSliceMulSliceAssign_f", // T[]=T[]*T[] + "_arraySliceSliceMulSliceAssign_i", "_arraySliceSliceMulSliceAssign_k", + "_arraySliceSliceMulSliceAssign_s", "_arraySliceSliceMulSliceAssign_t", + "_arraySliceSliceMulSliceAssign_u", "_arraySliceSliceMulSliceAssign_w", + + "_arraySliceSliceMulass_d", // T[]*=T[] + "_arraySliceSliceMulass_f", // T[]*=T[] + "_arraySliceSliceMulass_i", "_arraySliceSliceMulass_k", + "_arraySliceSliceMulass_s", "_arraySliceSliceMulass_t", + "_arraySliceSliceMulass_u", "_arraySliceSliceMulass_w", + }; + char *name = fd->ident->toChars(); + int i = + binary(name, libArrayopFuncs, sizeof(libArrayopFuncs) / sizeof(char *)); + if (i != -1) + return 1; + +#ifdef DEBUG // Make sure our array is alphabetized + for (i = 0; i < sizeof(libArrayopFuncs) / sizeof(char *); i++) { + if (strcmp(name, libArrayopFuncs[i]) == 0) + assert(0); + } +#endif + return 0; } diff --git a/gen/functions.h b/gen/functions.h index 1efbdd78e7..ce0d2d868b 100644 --- a/gen/functions.h +++ b/gen/functions.h @@ -23,24 +23,26 @@ struct IRAsmBlock; struct IrFuncTy; class Parameter; class Type; -namespace llvm -{ - class FunctionType; - class Value; +namespace llvm { +class FunctionType; +class Value; } -llvm::FunctionType* DtoFunctionType(Type* t, IrFuncTy &irFty, Type* thistype, Type* nesttype, - bool isMain = false, bool isCtor = false, bool isIntrinsic = false); -llvm::FunctionType* DtoFunctionType(FuncDeclaration* fdecl); +llvm::FunctionType *DtoFunctionType(Type *t, IrFuncTy &irFty, Type *thistype, + Type *nesttype, bool isMain = false, + bool isCtor = false, + bool isIntrinsic = false); +llvm::FunctionType *DtoFunctionType(FuncDeclaration *fdecl); -void DtoResolveFunction(FuncDeclaration* fdecl); -void DtoDeclareFunction(FuncDeclaration* fdecl); -void DtoDefineFunction(FuncDeclaration* fd); +void DtoResolveFunction(FuncDeclaration *fdecl); +void DtoDeclareFunction(FuncDeclaration *fdecl); +void DtoDefineFunction(FuncDeclaration *fd); -void DtoDefineNakedFunction(FuncDeclaration* fd); -void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc& loc, FuncDeclaration* fdecl); +void DtoDefineNakedFunction(FuncDeclaration *fd); +void emitABIReturnAsmStmt(IRAsmBlock *asmblock, Loc &loc, + FuncDeclaration *fdecl); -DValue* DtoArgument(Parameter* fnarg, Expression* argexp); +DValue *DtoArgument(Parameter *fnarg, Expression *argexp); // Search for a druntime array op int isDruntimeArrayOp(FuncDeclaration *fd); diff --git a/gen/inlineir.cpp b/gen/inlineir.cpp index a03c49e35f..72ebeb0864 100644 --- a/gen/inlineir.cpp +++ b/gen/inlineir.cpp @@ -10,88 +10,86 @@ #include "llvm/AsmParser/Parser.h" #include "llvm/Linker/Linker.h" -llvm::Function* DtoInlineIRFunction(FuncDeclaration* fdecl) -{ - const char* mangled_name = mangleExact(fdecl); - TemplateInstance* tinst = fdecl->parent->isTemplateInstance(); - assert(tinst); +llvm::Function *DtoInlineIRFunction(FuncDeclaration *fdecl) { + const char *mangled_name = mangleExact(fdecl); + TemplateInstance *tinst = fdecl->parent->isTemplateInstance(); + assert(tinst); - Objects& objs = tinst->tdtypes; - assert(objs.dim == 3); + Objects &objs = tinst->tdtypes; + assert(objs.dim == 3); - Expression* a0 = isExpression(objs[0]); - assert(a0); - StringExp* strexp = a0->toStringExp(); - assert(strexp); - assert(strexp->sz == 1); - std::string code(static_cast(strexp->string), strexp->len); + Expression *a0 = isExpression(objs[0]); + assert(a0); + StringExp *strexp = a0->toStringExp(); + assert(strexp); + assert(strexp->sz == 1); + std::string code(static_cast(strexp->string), strexp->len); - Type* ret = isType(objs[1]); - assert(ret); + Type *ret = isType(objs[1]); + assert(ret); - Tuple* a2 = isTuple(objs[2]); - assert(a2); - Objects& arg_types = a2->objects; + Tuple *a2 = isTuple(objs[2]); + assert(a2); + Objects &arg_types = a2->objects; - std::string str; - llvm::raw_string_ostream stream(str); - stream << "define " << *DtoType(ret) << " @" << mangled_name << "("; + std::string str; + llvm::raw_string_ostream stream(str); + stream << "define " << *DtoType(ret) << " @" << mangled_name << "("; - for(size_t i = 0; ;) - { - Type* ty = isType(arg_types[i]); - //assert(ty); - if(!ty) - { - error(tinst->loc, - "All parameters of a template defined with pragma llvm_inline_ir, except for the first one, should be types"); - fatal(); - } - stream << *DtoType(ty); - - i++; - if(i >= arg_types.dim) - break; - - stream << ", "; + for (size_t i = 0;;) { + Type *ty = isType(arg_types[i]); + // assert(ty); + if (!ty) { + error(tinst->loc, "All parameters of a template defined with pragma " + "llvm_inline_ir, except for the first one, should be " + "types"); + fatal(); } + stream << *DtoType(ty); - if(ret->ty == Tvoid) - code.append("\nret void"); + i++; + if (i >= arg_types.dim) + break; - stream << ")\n{\n" << code << "\n}"; + stream << ", "; + } - llvm::SMDiagnostic err; + if (ret->ty == Tvoid) + code.append("\nret void"); + + stream << ")\n{\n" << code << "\n}"; + + llvm::SMDiagnostic err; #if LDC_LLVM_VER >= 306 - std::unique_ptr m = llvm::parseAssemblyString( - stream.str().c_str(), err, gIR->context()); + std::unique_ptr m = + llvm::parseAssemblyString(stream.str().c_str(), err, gIR->context()); #else - llvm::Module* m = llvm::ParseAssemblyString( - stream.str().c_str(), NULL, err, gIR->context()); + llvm::Module *m = llvm::ParseAssemblyString(stream.str().c_str(), NULL, err, + gIR->context()); #endif - std::string errstr = err.getMessage(); - if(errstr != "") - error(tinst->loc, - "can't parse inline LLVM IR:\n%s\n%s\n%s\nThe input string was: \n%s", - err.getLineContents().str().c_str(), - (std::string(err.getColumnNo(), ' ') + '^').c_str(), - errstr.c_str(), stream.str().c_str()); + std::string errstr = err.getMessage(); + if (errstr != "") + error(tinst->loc, + "can't parse inline LLVM IR:\n%s\n%s\n%s\nThe input string was: \n%s", + err.getLineContents().str().c_str(), + (std::string(err.getColumnNo(), ' ') + '^').c_str(), errstr.c_str(), + stream.str().c_str()); #if LDC_LLVM_VER >= 306 - llvm::Linker(&gIR->module).linkInModule(m.get()); + llvm::Linker(&gIR->module).linkInModule(m.get()); #else - std::string errstr2 = ""; - llvm::Linker(&gIR->module).linkInModule(m, &errstr2); - if(errstr2 != "") - error(tinst->loc, - "Error when linking in llvm inline ir: %s", errstr2.c_str()); + std::string errstr2 = ""; + llvm::Linker(&gIR->module).linkInModule(m, &errstr2); + if (errstr2 != "") + error(tinst->loc, "Error when linking in llvm inline ir: %s", + errstr2.c_str()); #endif - LLFunction* fun = gIR->module.getFunction(mangled_name); - fun->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage); - SET_COMDAT(fun, gIR->module); - fun->addFnAttr(LLAttribute::AlwaysInline); - return fun; + LLFunction *fun = gIR->module.getFunction(mangled_name); + fun->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage); + SET_COMDAT(fun, gIR->module); + fun->addFnAttr(LLAttribute::AlwaysInline); + return fun; } diff --git a/gen/inlineir.h b/gen/inlineir.h index 33caa38d98..4fb20c991e 100644 --- a/gen/inlineir.h +++ b/gen/inlineir.h @@ -16,9 +16,9 @@ class FuncDeclaration; namespace llvm { - class Function; +class Function; } -llvm::Function* DtoInlineIRFunction(FuncDeclaration* fdecl); +llvm::Function *DtoInlineIRFunction(FuncDeclaration *fdecl); #endif diff --git a/gen/irstate.cpp b/gen/irstate.cpp index b990a3bf67..21ef8530d0 100644 --- a/gen/irstate.cpp +++ b/gen/irstate.cpp @@ -16,128 +16,113 @@ #include "ir/irfunction.h" #include -IRState* gIR = 0; -llvm::TargetMachine* gTargetMachine = 0; -const llvm::DataLayout* gDataLayout = 0; -TargetABI* gABI = 0; +IRState *gIR = 0; +llvm::TargetMachine *gTargetMachine = 0; +const llvm::DataLayout *gDataLayout = 0; +TargetABI *gABI = 0; ////////////////////////////////////////////////////////////////////////////////////////// -IRScope::IRScope() - : builder(gIR->context()) -{ - begin = NULL; -} +IRScope::IRScope() : builder(gIR->context()) { begin = NULL; } -IRScope::IRScope(llvm::BasicBlock* b) - : begin(b), builder(b) {} +IRScope::IRScope(llvm::BasicBlock *b) : begin(b), builder(b) {} -const IRScope& IRScope::operator=(const IRScope& rhs) -{ - begin = rhs.begin; - builder.SetInsertPoint(begin); - return *this; +const IRScope &IRScope::operator=(const IRScope &rhs) { + begin = rhs.begin; + builder.SetInsertPoint(begin); + return *this; } ////////////////////////////////////////////////////////////////////////////////////////// IRState::IRState(const char *name, llvm::LLVMContext &context) - : module(name, context), DBuilder(this) -{ - mutexType = NULL; - moduleRefType = NULL; + : module(name, context), DBuilder(this) { + mutexType = NULL; + moduleRefType = NULL; - dmodule = 0; - mainFunc = 0; - ir.state = this; - asmBlock = NULL; + dmodule = 0; + mainFunc = 0; + ir.state = this; + asmBlock = NULL; } -IrFunction* IRState::func() -{ - assert(!functions.empty() && "Function stack is empty!"); - return functions.back(); +IrFunction *IRState::func() { + assert(!functions.empty() && "Function stack is empty!"); + return functions.back(); } -llvm::Function* IRState::topfunc() -{ - assert(!functions.empty() && "Function stack is empty!"); - return functions.back()->func; +llvm::Function *IRState::topfunc() { + assert(!functions.empty() && "Function stack is empty!"); + return functions.back()->func; } -llvm::Instruction* IRState::topallocapoint() -{ - assert(!functions.empty() && "AllocaPoint stack is empty!"); - return functions.back()->allocapoint; +llvm::Instruction *IRState::topallocapoint() { + assert(!functions.empty() && "AllocaPoint stack is empty!"); + return functions.back()->allocapoint; } -IRScope& IRState::scope() -{ - assert(!scopes.empty()); - return scopes.back(); +IRScope &IRState::scope() { + assert(!scopes.empty()); + return scopes.back(); } -llvm::BasicBlock* IRState::scopebb() -{ - IRScope& s = scope(); - assert(s.begin); - return s.begin; +llvm::BasicBlock *IRState::scopebb() { + IRScope &s = scope(); + assert(s.begin); + return s.begin; } -bool IRState::scopereturned() -{ - //return scope().returned; - return !scopebb()->empty() && scopebb()->back().isTerminator(); +bool IRState::scopereturned() { + // return scope().returned; + return !scopebb()->empty() && scopebb()->back().isTerminator(); } -LLCallSite IRState::CreateCallOrInvoke(LLValue* Callee, const char* Name) -{ - LLSmallVector args; - return func()->scopes->callOrInvoke(Callee, args, Name); +LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, const char *Name) { + LLSmallVector args; + return func()->scopes->callOrInvoke(Callee, args, Name); } -LLCallSite IRState::CreateCallOrInvoke(LLValue* Callee, LLValue* Arg1, const char* Name) -{ - LLValue* args[] = { Arg1 }; - return func()->scopes->callOrInvoke(Callee, args, Name); +LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, + const char *Name) { + LLValue *args[] = {Arg1}; + return func()->scopes->callOrInvoke(Callee, args, Name); } -LLCallSite IRState::CreateCallOrInvoke(LLValue* Callee, LLValue* Arg1, LLValue* Arg2, const char* Name) -{ - LLValue* args[] = { Arg1, Arg2 }; - return func()->scopes->callOrInvoke(Callee, args, Name); +LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, + LLValue *Arg2, const char *Name) { + LLValue *args[] = {Arg1, Arg2}; + return func()->scopes->callOrInvoke(Callee, args, Name); } -LLCallSite IRState::CreateCallOrInvoke(LLValue* Callee, LLValue* Arg1, LLValue* Arg2, LLValue* Arg3, const char* Name) -{ - LLValue* args[] = { Arg1, Arg2, Arg3 }; - return func()->scopes->callOrInvoke(Callee, args, Name); +LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, + LLValue *Arg2, LLValue *Arg3, + const char *Name) { + LLValue *args[] = {Arg1, Arg2, Arg3}; + return func()->scopes->callOrInvoke(Callee, args, Name); } -LLCallSite IRState::CreateCallOrInvoke(LLValue* Callee, LLValue* Arg1, LLValue* Arg2, LLValue* Arg3, LLValue* Arg4, const char* Name) -{ - LLValue* args[] = { Arg1, Arg2, Arg3, Arg4 }; - return func()->scopes->callOrInvoke(Callee, args, Name); +LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, + LLValue *Arg2, LLValue *Arg3, + LLValue *Arg4, const char *Name) { + LLValue *args[] = {Arg1, Arg2, Arg3, Arg4}; + return func()->scopes->callOrInvoke(Callee, args, Name); } -bool IRState::emitArrayBoundsChecks() -{ - if (global.params.useArrayBounds != BOUNDSCHECKsafeonly) - { - return global.params.useArrayBounds == BOUNDSCHECKon; - } +bool IRState::emitArrayBoundsChecks() { + if (global.params.useArrayBounds != BOUNDSCHECKsafeonly) { + return global.params.useArrayBounds == BOUNDSCHECKon; + } - // Safe functions only. - if (functions.empty()) return false; + // Safe functions only. + if (functions.empty()) + return false; - Type* t = func()->decl->type; - return t->ty == Tfunction && ((TypeFunction*)t)->trust == TRUSTsafe; + Type *t = func()->decl->type; + return t->ty == Tfunction && ((TypeFunction *)t)->trust == TRUSTsafe; } - ////////////////////////////////////////////////////////////////////////////////////////// -IRBuilder<>* IRBuilderHelper::operator->() -{ - IRBuilder<>& b = state->scope().builder; - assert(b.GetInsertBlock() != NULL); - return &b; +IRBuilder<> *IRBuilderHelper::operator->() { + IRBuilder<> &b = state->scope().builder; + assert(b.GetInsertBlock() != NULL); + return &b; } diff --git a/gen/irstate.h b/gen/irstate.h index f05c4de0e8..ebc9ec5ec6 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -29,18 +29,18 @@ #include "llvm/IR/CallSite.h" namespace llvm { - class LLVMContext; - class TargetMachine; +class LLVMContext; +class TargetMachine; } // global ir state for current module struct IRState; struct TargetABI; -extern IRState* gIR; -extern llvm::TargetMachine* gTargetMachine; -extern const llvm::DataLayout* gDataLayout; -extern TargetABI* gABI; +extern IRState *gIR; +extern llvm::TargetMachine *gTargetMachine; +extern const llvm::DataLayout *gDataLayout; +extern TargetABI *gABI; class TypeFunction; class TypeStruct; @@ -55,133 +55,132 @@ struct IrFunction; struct IrModule; // represents a scope -struct IRScope -{ - llvm::BasicBlock* begin; - IRBuilder<> builder; +struct IRScope { + llvm::BasicBlock *begin; + IRBuilder<> builder; - IRScope(); - explicit IRScope(llvm::BasicBlock* b); + IRScope(); + explicit IRScope(llvm::BasicBlock *b); - const IRScope& operator=(const IRScope& rhs); + const IRScope &operator=(const IRScope &rhs); }; -struct IRBuilderHelper -{ - IRState* state; - IRBuilder<>* operator->(); +struct IRBuilderHelper { + IRState *state; + IRBuilder<> *operator->(); }; -struct IRAsmStmt -{ - IRAsmStmt() - : isBranchToLabel(NULL) {} +struct IRAsmStmt { + IRAsmStmt() : isBranchToLabel(NULL) {} - std::string code; - std::string out_c; - std::string in_c; - std::vector out; - std::vector in; + std::string code; + std::string out_c; + std::string in_c; + std::vector out; + std::vector in; - // if this is nonzero, it contains the target label - LabelDsymbol* isBranchToLabel; + // if this is nonzero, it contains the target label + LabelDsymbol *isBranchToLabel; }; -struct IRAsmBlock -{ - std::deque s; - std::set clobs; - size_t outputcount; +struct IRAsmBlock { + std::deque s; + std::set clobs; + size_t outputcount; - // stores the labels within the asm block - std::vector internalLabels; + // stores the labels within the asm block + std::vector internalLabels; - CompoundAsmStatement* asmBlock; - LLType* retty; - unsigned retn; - bool retemu; // emulate abi ret with a temporary - LLValue* (*retfixup)(IRBuilderHelper b, LLValue* orig); // Modifies retval + CompoundAsmStatement *asmBlock; + LLType *retty; + unsigned retn; + bool retemu; // emulate abi ret with a temporary + LLValue *(*retfixup)(IRBuilderHelper b, LLValue *orig); // Modifies retval - IRAsmBlock(CompoundAsmStatement* b) - : outputcount(0), asmBlock(b), retty(NULL), retn(0), retemu(false), - retfixup(NULL) - {} + IRAsmBlock(CompoundAsmStatement *b) + : outputcount(0), asmBlock(b), retty(NULL), retn(0), retemu(false), + retfixup(NULL) {} }; // represents the module -struct IRState -{ - IRState(const char *name, llvm::LLVMContext &context); +struct IRState { + IRState(const char *name, llvm::LLVMContext &context); - llvm::Module module; - llvm::LLVMContext& context() const { return module.getContext(); } + llvm::Module module; + llvm::LLVMContext &context() const { return module.getContext(); } - Module *dmodule; + Module *dmodule; - LLStructType* mutexType; - LLStructType* moduleRefType; + LLStructType *mutexType; + LLStructType *moduleRefType; - // functions - typedef std::vector FunctionVector; - FunctionVector functions; - IrFunction* func(); + // functions + typedef std::vector FunctionVector; + FunctionVector functions; + IrFunction *func(); - llvm::Function* topfunc(); - llvm::Instruction* topallocapoint(); + llvm::Function *topfunc(); + llvm::Instruction *topallocapoint(); - // The function containing the D main() body, if any (not the actual main() - // implicitly emitted). - llvm::Function* mainFunc; + // The function containing the D main() body, if any (not the actual main() + // implicitly emitted). + llvm::Function *mainFunc; - // basic block scopes - std::vector scopes; - IRScope& scope(); - llvm::BasicBlock* scopebb(); - bool scopereturned(); + // basic block scopes + std::vector scopes; + IRScope &scope(); + llvm::BasicBlock *scopebb(); + bool scopereturned(); - // create a call or invoke, depending on the landing pad info - llvm::CallSite CreateCallOrInvoke(LLValue* Callee, const char* Name=""); - llvm::CallSite CreateCallOrInvoke(LLValue* Callee, LLValue* Arg1, const char* Name=""); - llvm::CallSite CreateCallOrInvoke(LLValue* Callee, LLValue* Arg1, LLValue* Arg2, const char* Name=""); - llvm::CallSite CreateCallOrInvoke(LLValue* Callee, LLValue* Arg1, LLValue* Arg2, LLValue* Arg3, const char* Name=""); - llvm::CallSite CreateCallOrInvoke(LLValue* Callee, LLValue* Arg1, LLValue* Arg2, LLValue* Arg3, LLValue* Arg4, const char* Name=""); + // create a call or invoke, depending on the landing pad info + llvm::CallSite CreateCallOrInvoke(LLValue *Callee, const char *Name = ""); + llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, + const char *Name = ""); + llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, + LLValue *Arg2, const char *Name = ""); + llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, + LLValue *Arg2, LLValue *Arg3, + const char *Name = ""); + llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, + LLValue *Arg2, LLValue *Arg3, LLValue *Arg4, + const char *Name = ""); - // this holds the array being indexed or sliced so $ will work - // might be a better way but it works. problem is I only get a - // VarDeclaration for __dollar, but I can't see how to get the - // array pointer from this :( - std::vector arrays; + // this holds the array being indexed or sliced so $ will work + // might be a better way but it works. problem is I only get a + // VarDeclaration for __dollar, but I can't see how to get the + // array pointer from this :( + std::vector arrays; - // builder helper - IRBuilderHelper ir; + // builder helper + IRBuilderHelper ir; - // debug info helper - ldc::DIBuilder DBuilder; + // debug info helper + ldc::DIBuilder DBuilder; - // for inline asm - IRAsmBlock* asmBlock; - std::ostringstream nakedAsm; + // for inline asm + IRAsmBlock *asmBlock; + std::ostringstream nakedAsm; - // Globals to pin in the llvm.used array to make sure they are not - // eliminated. - std::vector usedArray; + // Globals to pin in the llvm.used array to make sure they are not + // eliminated. + std::vector usedArray; - /// Whether to emit array bounds checking in the current function. - bool emitArrayBoundsChecks(); + /// Whether to emit array bounds checking in the current function. + bool emitArrayBoundsChecks(); - // Global variables bound to string literals. Once created such a - // variable is reused whenever the same string literal is - // referenced in the module. Caching them per module prevents the - // duplication of identical literals. - llvm::StringMap stringLiteral1ByteCache; - llvm::StringMap stringLiteral2ByteCache; - llvm::StringMap stringLiteral4ByteCache; + // Global variables bound to string literals. Once created such a + // variable is reused whenever the same string literal is + // referenced in the module. Caching them per module prevents the + // duplication of identical literals. + llvm::StringMap stringLiteral1ByteCache; + llvm::StringMap stringLiteral2ByteCache; + llvm::StringMap stringLiteral4ByteCache; - /// Vector of options passed to the linker as metadata in object file. +/// Vector of options passed to the linker as metadata in object file. #if LDC_LLVM_VER >= 306 - llvm::SmallVector LinkerMetadataArgs; + llvm::SmallVector LinkerMetadataArgs; #else - llvm::SmallVector LinkerMetadataArgs; + llvm::SmallVector LinkerMetadataArgs; #endif }; diff --git a/gen/linkage.h b/gen/linkage.h index 7bbea3dc1b..49a8bbbd00 100644 --- a/gen/linkage.h +++ b/gen/linkage.h @@ -18,9 +18,9 @@ // Make it easier to test new linkage types -# define TYPEINFO_LINKAGE_TYPE LLGlobalValue::LinkOnceODRLinkage +#define TYPEINFO_LINKAGE_TYPE LLGlobalValue::LinkOnceODRLinkage // The One-Definition-Rule shouldn't matter for debug info, right? -# define DEBUGINFO_LINKONCE_LINKAGE_TYPE LLGlobalValue::LinkOnceAnyLinkage +#define DEBUGINFO_LINKONCE_LINKAGE_TYPE LLGlobalValue::LinkOnceAnyLinkage extern LLGlobalValue::LinkageTypes templateLinkage; diff --git a/gen/llvm.h b/gen/llvm.h index 1bfe945e29..990c829bc0 100644 --- a/gen/llvm.h +++ b/gen/llvm.h @@ -38,7 +38,8 @@ using llvm::IRBuilder; -#define GET_INTRINSIC_DECL(_X) (llvm::Intrinsic::getDeclaration(&gIR->module, llvm::Intrinsic:: _X )) +#define GET_INTRINSIC_DECL(_X) \ + (llvm::Intrinsic::getDeclaration(&gIR->module, llvm::Intrinsic::_X)) // shortcuts for the common llvm types diff --git a/gen/llvmcompat.h b/gen/llvmcompat.h index 64cc753dd7..cbee2a5db9 100644 --- a/gen/llvmcompat.h +++ b/gen/llvmcompat.h @@ -12,7 +12,6 @@ // //===----------------------------------------------------------------------===// - #ifdef _MSC_VER #pragma once #endif @@ -27,19 +26,19 @@ #define ADDRESS_SPACE 0 #ifndef __has_feature -# define __has_feature(x) 0 +#define __has_feature(x) 0 #endif -#if __has_feature(cxx_override_control) \ - || (defined(_MSC_VER) && _MSC_VER >= 1700) +#if __has_feature(cxx_override_control) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) #define LLVM_OVERRIDE override #else #define LLVM_OVERRIDE #endif -#if (__has_feature(cxx_rvalue_references) \ - || defined(__GXX_EXPERIMENTAL_CXX0X__) \ - || (defined(_MSC_VER) && _MSC_VER >= 1600)) +#if (__has_feature(cxx_rvalue_references) || \ + defined(__GXX_EXPERIMENTAL_CXX0X__) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600)) #define LLVM_HAS_RVALUE_REFERENCES 1 #else #define LLVM_HAS_RVALUE_REFERENCES 0 diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index afef209de9..6ad3d45e3d 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -39,19 +39,19 @@ #include "llvm/Support/CommandLine.h" -llvm::cl::opt clThreadModel("fthread-model", - llvm::cl::desc("Thread model"), +llvm::cl::opt clThreadModel( + "fthread-model", llvm::cl::desc("Thread model"), llvm::cl::init(llvm::GlobalVariable::GeneralDynamicTLSModel), - llvm::cl::values( - clEnumValN(llvm::GlobalVariable::GeneralDynamicTLSModel, "global-dynamic", - "Global dynamic TLS model (default)"), - clEnumValN(llvm::GlobalVariable::LocalDynamicTLSModel, "local-dynamic", - "Local dynamic TLS model"), - clEnumValN(llvm::GlobalVariable::InitialExecTLSModel, "initial-exec", - "Initial exec TLS model"), - clEnumValN(llvm::GlobalVariable::LocalExecTLSModel, "local-exec", - "Local exec TLS model"), - clEnumValEnd)); + llvm::cl::values(clEnumValN(llvm::GlobalVariable::GeneralDynamicTLSModel, + "global-dynamic", + "Global dynamic TLS model (default)"), + clEnumValN(llvm::GlobalVariable::LocalDynamicTLSModel, + "local-dynamic", "Local dynamic TLS model"), + clEnumValN(llvm::GlobalVariable::InitialExecTLSModel, + "initial-exec", "Initial exec TLS model"), + clEnumValN(llvm::GlobalVariable::LocalExecTLSModel, + "local-exec", "Local exec TLS model"), + clEnumValEnd)); Type *getTypeInfoType(Type *t, Scope *sc); @@ -60,76 +60,77 @@ Type *getTypeInfoType(Type *t, Scope *sc); // DYNAMIC MEMORY HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ -LLValue* DtoNew(Loc& loc, Type* newtype) -{ - // get runtime function - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_allocmemoryT"); - // get type info - LLConstant* ti = DtoTypeInfoOf(newtype); - assert(isaPointer(ti)); - // call runtime allocator - LLValue* mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_mem").getInstruction(); - // cast - return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_mem"); +LLValue *DtoNew(Loc &loc, Type *newtype) { + // get runtime function + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_allocmemoryT"); + // get type info + LLConstant *ti = DtoTypeInfoOf(newtype); + assert(isaPointer(ti)); + // call runtime allocator + LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_mem").getInstruction(); + // cast + return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_mem"); } -LLValue* DtoNewStruct(Loc& loc, TypeStruct* newtype) -{ - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, - newtype->isZeroInit(newtype->sym->loc) ? "_d_newitemT" : "_d_newitemiT"); - LLConstant* ti = DtoTypeInfoOf(newtype); - LLValue* mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_struct").getInstruction(); - return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_struct"); +LLValue *DtoNewStruct(Loc &loc, TypeStruct *newtype) { + llvm::Function *fn = LLVM_D_GetRuntimeFunction( + loc, gIR->module, + newtype->isZeroInit(newtype->sym->loc) ? "_d_newitemT" : "_d_newitemiT"); + LLConstant *ti = DtoTypeInfoOf(newtype); + LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_struct").getInstruction(); + return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_struct"); } -void DtoDeleteMemory(Loc& loc, DValue* ptr) -{ - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delmemory"); - LLValue* lval = (ptr->isLVal() ? ptr->getLVal() : makeLValue(loc, ptr)); - gIR->CreateCallOrInvoke(fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); +void DtoDeleteMemory(Loc &loc, DValue *ptr) { + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delmemory"); + LLValue *lval = (ptr->isLVal() ? ptr->getLVal() : makeLValue(loc, ptr)); + gIR->CreateCallOrInvoke( + fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); } -void DtoDeleteStruct(Loc& loc, DValue* ptr) -{ - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delstruct"); - LLValue* lval = (ptr->isLVal() ? ptr->getLVal() : makeLValue(loc, ptr)); - gIR->CreateCallOrInvoke( - fn, - DtoBitCast(lval, fn->getFunctionType()->getParamType(0)), - DtoBitCast(DtoTypeInfoOf(ptr->type->nextOf()), fn->getFunctionType()->getParamType(1)) - ); +void DtoDeleteStruct(Loc &loc, DValue *ptr) { + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delstruct"); + LLValue *lval = (ptr->isLVal() ? ptr->getLVal() : makeLValue(loc, ptr)); + gIR->CreateCallOrInvoke( + fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0)), + DtoBitCast(DtoTypeInfoOf(ptr->type->nextOf()), + fn->getFunctionType()->getParamType(1))); } -void DtoDeleteClass(Loc& loc, DValue* inst) -{ - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delclass"); - LLValue* lval = (inst->isLVal() ? inst->getLVal() : makeLValue(loc, inst)); - gIR->CreateCallOrInvoke(fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); +void DtoDeleteClass(Loc &loc, DValue *inst) { + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delclass"); + LLValue *lval = (inst->isLVal() ? inst->getLVal() : makeLValue(loc, inst)); + gIR->CreateCallOrInvoke( + fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); } -void DtoDeleteInterface(Loc& loc, DValue* inst) -{ - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delinterface"); - LLValue* lval = (inst->isLVal() ? inst->getLVal() : makeLValue(loc, inst)); - gIR->CreateCallOrInvoke(fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); +void DtoDeleteInterface(Loc &loc, DValue *inst) { + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delinterface"); + LLValue *lval = (inst->isLVal() ? inst->getLVal() : makeLValue(loc, inst)); + gIR->CreateCallOrInvoke( + fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); } -void DtoDeleteArray(Loc& loc, DValue* arr) -{ - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delarray_t"); - llvm::FunctionType* fty = fn->getFunctionType(); +void DtoDeleteArray(Loc &loc, DValue *arr) { + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_delarray_t"); + llvm::FunctionType *fty = fn->getFunctionType(); - // the TypeInfo argument must be null if the type has no dtor - Type* elementType = arr->type->nextOf(); - bool hasDtor = (elementType->toBasetype()->ty == Tstruct && elementType->needsDestruction()); - LLValue* typeInfo = (!hasDtor ? getNullPtr(fty->getParamType(1)) : DtoTypeInfoOf(elementType)); + // the TypeInfo argument must be null if the type has no dtor + Type *elementType = arr->type->nextOf(); + bool hasDtor = (elementType->toBasetype()->ty == Tstruct && + elementType->needsDestruction()); + LLValue *typeInfo = (!hasDtor ? getNullPtr(fty->getParamType(1)) + : DtoTypeInfoOf(elementType)); - LLValue* lval = (arr->isLVal() ? arr->getLVal() : makeLValue(loc, arr)); - gIR->CreateCallOrInvoke( - fn, - DtoBitCast(lval, fty->getParamType(0)), - DtoBitCast(typeInfo, fty->getParamType(1)) - ); + LLValue *lval = (arr->isLVal() ? arr->getLVal() : makeLValue(loc, arr)); + gIR->CreateCallOrInvoke(fn, DtoBitCast(lval, fty->getParamType(0)), + DtoBitCast(typeInfo, fty->getParamType(1))); } /****************************************************************************************/ @@ -137,17 +138,16 @@ void DtoDeleteArray(Loc& loc, DValue* arr) // ALIGNMENT HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ -unsigned DtoAlignment(Type* type) -{ - structalign_t alignment = type->alignment(); - if (alignment == STRUCTALIGN_DEFAULT) - alignment = type->alignsize(); - return (alignment == STRUCTALIGN_DEFAULT ? 0 : alignment); +unsigned DtoAlignment(Type *type) { + structalign_t alignment = type->alignment(); + if (alignment == STRUCTALIGN_DEFAULT) + alignment = type->alignsize(); + return (alignment == STRUCTALIGN_DEFAULT ? 0 : alignment); } -unsigned DtoAlignment(VarDeclaration* vd) -{ - return vd->alignment == STRUCTALIGN_DEFAULT ? DtoAlignment(vd->type) : vd->alignment; +unsigned DtoAlignment(VarDeclaration *vd) { + return vd->alignment == STRUCTALIGN_DEFAULT ? DtoAlignment(vd->type) + : vd->alignment; } /****************************************************************************************/ @@ -155,79 +155,75 @@ unsigned DtoAlignment(VarDeclaration* vd) // ALLOCA HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ -llvm::AllocaInst* DtoAlloca(Type* type, const char* name) -{ - return DtoRawAlloca(DtoMemType(type), DtoAlignment(type), name); +llvm::AllocaInst *DtoAlloca(Type *type, const char *name) { + return DtoRawAlloca(DtoMemType(type), DtoAlignment(type), name); } -llvm::AllocaInst* DtoAlloca(VarDeclaration* vd, const char* name) -{ - return DtoRawAlloca(DtoMemType(vd->type), DtoAlignment(vd), name); +llvm::AllocaInst *DtoAlloca(VarDeclaration *vd, const char *name) { + return DtoRawAlloca(DtoMemType(vd->type), DtoAlignment(vd), name); } -llvm::AllocaInst* DtoArrayAlloca(Type* type, unsigned arraysize, const char* name) -{ - LLType* lltype = DtoType(type); - llvm::AllocaInst* ai = new llvm::AllocaInst( - lltype, DtoConstUint(arraysize), name, gIR->topallocapoint()); - ai->setAlignment(DtoAlignment(type)); - return ai; +llvm::AllocaInst *DtoArrayAlloca(Type *type, unsigned arraysize, + const char *name) { + LLType *lltype = DtoType(type); + llvm::AllocaInst *ai = new llvm::AllocaInst(lltype, DtoConstUint(arraysize), + name, gIR->topallocapoint()); + ai->setAlignment(DtoAlignment(type)); + return ai; } -llvm::AllocaInst* DtoRawAlloca(LLType* lltype, size_t alignment, const char* name) -{ - llvm::AllocaInst* ai = new llvm::AllocaInst(lltype, name, gIR->topallocapoint()); - if (alignment) - ai->setAlignment(alignment); - return ai; +llvm::AllocaInst *DtoRawAlloca(LLType *lltype, size_t alignment, + const char *name) { + llvm::AllocaInst *ai = + new llvm::AllocaInst(lltype, name, gIR->topallocapoint()); + if (alignment) + ai->setAlignment(alignment); + return ai; } -LLValue* DtoGcMalloc(Loc& loc, LLType* lltype, const char* name) -{ - // get runtime function - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_allocmemory"); - // parameters - LLValue *size = DtoConstSize_t(getTypeAllocSize(lltype)); - // call runtime allocator - LLValue* mem = gIR->CreateCallOrInvoke(fn, size, name).getInstruction(); - // cast - return DtoBitCast(mem, getPtrToType(lltype), name); +LLValue *DtoGcMalloc(Loc &loc, LLType *lltype, const char *name) { + // get runtime function + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_allocmemory"); + // parameters + LLValue *size = DtoConstSize_t(getTypeAllocSize(lltype)); + // call runtime allocator + LLValue *mem = gIR->CreateCallOrInvoke(fn, size, name).getInstruction(); + // cast + return DtoBitCast(mem, getPtrToType(lltype), name); } -LLValue* DtoAllocaDump(DValue* val, const char* name) -{ - return DtoAllocaDump(val->getRVal(), val->getType(), name); +LLValue *DtoAllocaDump(DValue *val, const char *name) { + return DtoAllocaDump(val->getRVal(), val->getType(), name); } -LLValue* DtoAllocaDump(DValue* val, Type* asType, const char* name) -{ - return DtoAllocaDump(val->getRVal(), asType, name); +LLValue *DtoAllocaDump(DValue *val, Type *asType, const char *name) { + return DtoAllocaDump(val->getRVal(), asType, name); } -LLValue* DtoAllocaDump(DValue* val, LLType* asType, int alignment, const char* name) -{ - return DtoAllocaDump(val->getRVal(), asType, alignment, name); +LLValue *DtoAllocaDump(DValue *val, LLType *asType, int alignment, + const char *name) { + return DtoAllocaDump(val->getRVal(), asType, alignment, name); } -LLValue* DtoAllocaDump(LLValue* val, int alignment, const char* name) -{ - return DtoAllocaDump(val, val->getType(), alignment, name); +LLValue *DtoAllocaDump(LLValue *val, int alignment, const char *name) { + return DtoAllocaDump(val, val->getType(), alignment, name); } -LLValue* DtoAllocaDump(LLValue* val, Type* asType, const char* name) -{ - return DtoAllocaDump(val, DtoType(asType), DtoAlignment(asType), name); +LLValue *DtoAllocaDump(LLValue *val, Type *asType, const char *name) { + return DtoAllocaDump(val, DtoType(asType), DtoAlignment(asType), name); } -LLValue* DtoAllocaDump(LLValue* val, LLType* asType, int alignment, const char* name) -{ - LLType* valType = i1ToI8(voidToI8(val->getType())); - asType = i1ToI8(voidToI8(asType)); - LLType* allocaType = ( - getTypeStoreSize(valType) <= getTypeAllocSize(asType) ? asType : valType); - LLValue* mem = DtoRawAlloca(allocaType, alignment, name); - DtoStoreZextI8(val, DtoBitCast(mem, valType->getPointerTo())); - return DtoBitCast(mem, asType->getPointerTo()); +LLValue *DtoAllocaDump(LLValue *val, LLType *asType, int alignment, + const char *name) { + LLType *valType = i1ToI8(voidToI8(val->getType())); + asType = i1ToI8(voidToI8(asType)); + LLType *allocaType = + (getTypeStoreSize(valType) <= getTypeAllocSize(asType) ? asType + : valType); + LLValue *mem = DtoRawAlloca(allocaType, alignment, name); + DtoStoreZextI8(val, DtoBitCast(mem, valType->getPointerTo())); + return DtoBitCast(mem, asType->getPointerTo()); } /****************************************************************************************/ @@ -235,32 +231,30 @@ LLValue* DtoAllocaDump(LLValue* val, LLType* asType, int alignment, const char* // ASSERT HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ -void DtoAssert(Module* M, Loc& loc, DValue* msg) -{ - // func - const char* fname = msg ? "_d_assert_msg" : "_d_assert"; - llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, fname); +void DtoAssert(Module *M, Loc &loc, DValue *msg) { + // func + const char *fname = msg ? "_d_assert_msg" : "_d_assert"; + llvm::Function *fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, fname); - // Arguments - llvm::SmallVector args; + // Arguments + llvm::SmallVector args; - // msg param - if (msg) - { - args.push_back(msg->getRVal()); - } + // msg param + if (msg) { + args.push_back(msg->getRVal()); + } - // file param - args.push_back(DtoModuleFileName(M, loc)); + // file param + args.push_back(DtoModuleFileName(M, loc)); - // line param - args.push_back(DtoConstUint(loc.linnum)); + // line param + args.push_back(DtoConstUint(loc.linnum)); - // call - gIR->func()->scopes->callOrInvoke(fn, args); + // call + gIR->func()->scopes->callOrInvoke(fn, args); - // after assert is always unreachable - gIR->ir->CreateUnreachable(); + // after assert is always unreachable + gIR->ir->CreateUnreachable(); } /****************************************************************************************/ @@ -268,28 +262,25 @@ void DtoAssert(Module* M, Loc& loc, DValue* msg) // Module file name ////////////////////////////////////////////////////////////////////////////////////////*/ -LLValue *DtoModuleFileName(Module* M, const Loc& loc) -{ - return DtoConstString(loc.filename ? loc.filename : - M->srcfile->name->toChars()); +LLValue *DtoModuleFileName(Module *M, const Loc &loc) { + return DtoConstString(loc.filename ? loc.filename + : M->srcfile->name->toChars()); } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // GOTO HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ -void DtoGoto(Loc &loc, LabelDsymbol *target) -{ - assert(!gIR->scopereturned()); +void DtoGoto(Loc &loc, LabelDsymbol *target) { + assert(!gIR->scopereturned()); - LabelStatement *lblstmt = target->statement; - if (!lblstmt) - { - error(loc, "the label %s does not exist", target->ident->toChars()); - fatal(); - } + LabelStatement *lblstmt = target->statement; + if (!lblstmt) { + error(loc, "the label %s does not exist", target->ident->toChars()); + fatal(); + } - gIR->func()->scopes->jumpToLabel(loc, target->ident); + gIR->func()->scopes->jumpToLabel(loc, target->ident); } /*//////////////////////////////////////////////////////////////////////////////////////// @@ -298,85 +289,80 @@ void DtoGoto(Loc &loc, LabelDsymbol *target) // is this a good approach at all ? -void DtoAssign(Loc& loc, DValue* lhs, DValue* rhs, int op, bool canSkipPostblit) -{ - IF_LOG Logger::println("DtoAssign()"); - LOG_SCOPE; +void DtoAssign(Loc &loc, DValue *lhs, DValue *rhs, int op, + bool canSkipPostblit) { + IF_LOG Logger::println("DtoAssign()"); + LOG_SCOPE; - Type* t = lhs->getType()->toBasetype(); - Type* t2 = rhs->getType()->toBasetype(); + Type *t = lhs->getType()->toBasetype(); + Type *t2 = rhs->getType()->toBasetype(); - assert(t->ty != Tvoid && "Cannot assign values of type void."); + assert(t->ty != Tvoid && "Cannot assign values of type void."); - if (t->ty == Tbool) { - DtoStoreZextI8(rhs->getRVal(), lhs->getLVal()); - } - else if (t->ty == Tstruct) { - // don't copy anything to empty structs - if (static_cast(t)->sym->fields.dim > 0) { - llvm::Value* src = rhs->getRVal(); - llvm::Value* dst = lhs->getLVal(); + if (t->ty == Tbool) { + DtoStoreZextI8(rhs->getRVal(), lhs->getLVal()); + } else if (t->ty == Tstruct) { + // don't copy anything to empty structs + if (static_cast(t)->sym->fields.dim > 0) { + llvm::Value *src = rhs->getRVal(); + llvm::Value *dst = lhs->getLVal(); - // Check whether source and destination values are the same at compile - // time as to not emit an invalid (overlapping) memcpy on trivial - // struct self-assignments like 'A a; a = a;'. - if (src != dst) - DtoAggrCopy(dst, src); - } + // Check whether source and destination values are the same at compile + // time as to not emit an invalid (overlapping) memcpy on trivial + // struct self-assignments like 'A a; a = a;'. + if (src != dst) + DtoAggrCopy(dst, src); } - else if (t->ty == Tarray || t->ty == Tsarray) { - DtoArrayAssign(loc, lhs, rhs, op, canSkipPostblit); + } else if (t->ty == Tarray || t->ty == Tsarray) { + DtoArrayAssign(loc, lhs, rhs, op, canSkipPostblit); + } else if (t->ty == Tdelegate) { + LLValue *l = lhs->getLVal(); + LLValue *r = rhs->getRVal(); + IF_LOG { + Logger::cout() << "lhs: " << *l << '\n'; + Logger::cout() << "rhs: " << *r << '\n'; } - else if (t->ty == Tdelegate) { - LLValue* l = lhs->getLVal(); - LLValue* r = rhs->getRVal(); - IF_LOG { - Logger::cout() << "lhs: " << *l << '\n'; - Logger::cout() << "rhs: " << *r << '\n'; - } - DtoStore(r, l); + DtoStore(r, l); + } else if (t->ty == Tclass) { + assert(t2->ty == Tclass); + LLValue *l = lhs->getLVal(); + LLValue *r = rhs->getRVal(); + IF_LOG { + Logger::cout() << "l : " << *l << '\n'; + Logger::cout() << "r : " << *r << '\n'; } - else if (t->ty == Tclass) { - assert(t2->ty == Tclass); - LLValue* l = lhs->getLVal(); - LLValue* r = rhs->getRVal(); - IF_LOG { - Logger::cout() << "l : " << *l << '\n'; - Logger::cout() << "r : " << *r << '\n'; - } - r = DtoBitCast(r, l->getType()->getContainedType(0)); - DtoStore(r, l); + r = DtoBitCast(r, l->getType()->getContainedType(0)); + DtoStore(r, l); + } else if (t->iscomplex()) { + LLValue *dst = lhs->getLVal(); + LLValue *src = DtoCast(loc, rhs, lhs->getType())->getRVal(); + DtoStore(src, dst); + } else { + LLValue *l = lhs->getLVal(); + LLValue *r = rhs->getRVal(); + IF_LOG { + Logger::cout() << "lhs: " << *l << '\n'; + Logger::cout() << "rhs: " << *r << '\n'; } - else if (t->iscomplex()) { - LLValue* dst = lhs->getLVal(); - LLValue* src = DtoCast(loc, rhs, lhs->getType())->getRVal(); - DtoStore(src, dst); - } - else { - LLValue* l = lhs->getLVal(); - LLValue* r = rhs->getRVal(); - IF_LOG { - Logger::cout() << "lhs: " << *l << '\n'; - Logger::cout() << "rhs: " << *r << '\n'; - } - LLType* lit = l->getType()->getContainedType(0); - if (r->getType() != lit) { - r = DtoCast(loc, rhs, lhs->getType())->getRVal(); - IF_LOG { - Logger::println("Type mismatch, really assigning:"); - LOG_SCOPE - Logger::cout() << "lhs: " << *l << '\n'; - Logger::cout() << "rhs: " << *r << '\n'; - } + LLType *lit = l->getType()->getContainedType(0); + if (r->getType() != lit) { + r = DtoCast(loc, rhs, lhs->getType())->getRVal(); + IF_LOG { + Logger::println("Type mismatch, really assigning:"); + LOG_SCOPE + Logger::cout() << "lhs: " << *l << '\n'; + Logger::cout() << "rhs: " << *r << '\n'; + } #if 1 - if(r->getType() != lit) // It's wierd but it happens. TODO: try to remove this hack - r = DtoBitCast(r, lit); + if (r->getType() != + lit) // It's wierd but it happens. TODO: try to remove this hack + r = DtoBitCast(r, lit); #else - assert(r->getType() == lit); + assert(r->getType() == lit); #endif - } - gIR->ir->CreateStore(r, l); } + gIR->ir->CreateStore(r, l); + } } /****************************************************************************************/ @@ -384,397 +370,335 @@ void DtoAssign(Loc& loc, DValue* lhs, DValue* rhs, int op, bool canSkipPostblit) // NULL VALUE HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ -DValue* DtoNullValue(Type* type, Loc loc) -{ - Type* basetype = type->toBasetype(); - TY basety = basetype->ty; - LLType* lltype = DtoType(basetype); +DValue *DtoNullValue(Type *type, Loc loc) { + Type *basetype = type->toBasetype(); + TY basety = basetype->ty; + LLType *lltype = DtoType(basetype); - // complex, needs to be first since complex are also floating - if (basetype->iscomplex()) - { - LLType* basefp = DtoComplexBaseType(basetype); - LLValue* res = DtoAggrPair(DtoType(type), LLConstant::getNullValue(basefp), LLConstant::getNullValue(basefp)); - return new DImValue(type, res); - } - // integer, floating, pointer, assoc array, delegate and class have no special representation - else if (basetype->isintegral() || - basetype->isfloating() || - basety == Tpointer || - basety == Tclass || - basety == Tdelegate || - basety == Taarray) - { - return new DConstValue(type, LLConstant::getNullValue(lltype)); - } - // dynamic array - else if (basety == Tarray) - { - LLValue* len = DtoConstSize_t(0); - LLValue* ptr = getNullPtr(DtoPtrToType(basetype->nextOf())); - return new DSliceValue(type, len, ptr); - } - else - { - error(loc, "null not known for type '%s'", type->toChars()); - fatal(); - } + // complex, needs to be first since complex are also floating + if (basetype->iscomplex()) { + LLType *basefp = DtoComplexBaseType(basetype); + LLValue *res = DtoAggrPair(DtoType(type), LLConstant::getNullValue(basefp), + LLConstant::getNullValue(basefp)); + return new DImValue(type, res); + } + // integer, floating, pointer, assoc array, delegate and class have no special + // representation + else if (basetype->isintegral() || basetype->isfloating() || + basety == Tpointer || basety == Tclass || basety == Tdelegate || + basety == Taarray) { + return new DConstValue(type, LLConstant::getNullValue(lltype)); + } + // dynamic array + else if (basety == Tarray) { + LLValue *len = DtoConstSize_t(0); + LLValue *ptr = getNullPtr(DtoPtrToType(basetype->nextOf())); + return new DSliceValue(type, len, ptr); + } else { + error(loc, "null not known for type '%s'", type->toChars()); + fatal(); + } } - /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // CASTING HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ -DValue* DtoCastInt(Loc& loc, DValue* val, Type* _to) -{ - LLType* tolltype = DtoType(_to); +DValue *DtoCastInt(Loc &loc, DValue *val, Type *_to) { + LLType *tolltype = DtoType(_to); - Type* to = _to->toBasetype(); - Type* from = val->getType()->toBasetype(); - assert(from->isintegral()); - - LLValue* rval = val->getRVal(); - if (rval->getType() == tolltype) { - return new DImValue(_to, rval); - } - - size_t fromsz = from->size(); - size_t tosz = to->size(); - - if (to->ty == Tbool) { - LLValue* zero = LLConstantInt::get(rval->getType(), 0, false); - rval = gIR->ir->CreateICmpNE(rval, zero); - } - else if (to->isintegral()) { - if (fromsz < tosz || from->ty == Tbool) { - IF_LOG Logger::cout() << "cast to: " << *tolltype << '\n'; - if (isLLVMUnsigned(from) || from->ty == Tbool) { - rval = new llvm::ZExtInst(rval, tolltype, "", gIR->scopebb()); - } else { - rval = new llvm::SExtInst(rval, tolltype, "", gIR->scopebb()); - } - } - else if (fromsz > tosz) { - rval = new llvm::TruncInst(rval, tolltype, "", gIR->scopebb()); - } - else { - rval = DtoBitCast(rval, tolltype); - } - } - else if (to->iscomplex()) { - return DtoComplex(loc, to, val); - } - else if (to->isfloating()) { - if (from->isunsigned()) { - rval = new llvm::UIToFPInst(rval, tolltype, "", gIR->scopebb()); - } - else { - rval = new llvm::SIToFPInst(rval, tolltype, "", gIR->scopebb()); - } - } - else if (to->ty == Tpointer) { - IF_LOG Logger::cout() << "cast pointer: " << *tolltype << '\n'; - rval = gIR->ir->CreateIntToPtr(rval, tolltype); - } - else { - error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), _to->toChars()); - fatal(); - } + Type *to = _to->toBasetype(); + Type *from = val->getType()->toBasetype(); + assert(from->isintegral()); + LLValue *rval = val->getRVal(); + if (rval->getType() == tolltype) { return new DImValue(_to, rval); -} + } -DValue* DtoCastPtr(Loc& loc, DValue* val, Type* to) -{ - LLType* tolltype = DtoType(to); + size_t fromsz = from->size(); + size_t tosz = to->size(); - Type* totype = to->toBasetype(); - Type* fromtype = val->getType()->toBasetype(); - assert(fromtype->ty == Tpointer || fromtype->ty == Tfunction); - - LLValue* rval; - - if (totype->ty == Tpointer || totype->ty == Tclass) { - LLValue* src = val->getRVal(); - IF_LOG { - Logger::cout() << "src: " << *src << '\n'; - Logger::cout() << "to type: " << *tolltype << '\n'; - } - rval = DtoBitCast(src, tolltype); - } - else if (totype->ty == Tbool) { - LLValue* src = val->getRVal(); - LLValue* zero = LLConstant::getNullValue(src->getType()); - rval = gIR->ir->CreateICmpNE(src, zero); - } - else if (totype->isintegral()) { - rval = new llvm::PtrToIntInst(val->getRVal(), tolltype, "", gIR->scopebb()); - } - else { - error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); - fatal(); - } - - return new DImValue(to, rval); -} - -DValue* DtoCastFloat(Loc& loc, DValue* val, Type* to) -{ - if (val->getType() == to) - return val; - - LLType* tolltype = DtoType(to); - - Type* totype = to->toBasetype(); - Type* fromtype = val->getType()->toBasetype(); - assert(fromtype->isfloating()); - - size_t fromsz = fromtype->size(); - size_t tosz = totype->size(); - - LLValue* rval; - - if (totype->ty == Tbool) { - rval = val->getRVal(); - LLValue* zero = LLConstant::getNullValue(rval->getType()); - rval = gIR->ir->CreateFCmpUNE(rval, zero); - } - else if (totype->iscomplex()) { - return DtoComplex(loc, to, val); - } - else if (totype->isfloating()) { - if (fromsz == tosz) { - rval = val->getRVal(); - assert(rval->getType() == tolltype); - } - else if (fromsz < tosz) { - rval = new llvm::FPExtInst(val->getRVal(), tolltype, "", gIR->scopebb()); - } - else if (fromsz > tosz) { - rval = new llvm::FPTruncInst(val->getRVal(), tolltype, "", gIR->scopebb()); - } - else { - error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); - fatal(); - } - } - else if (totype->isintegral()) { - if (totype->isunsigned()) { - rval = new llvm::FPToUIInst(val->getRVal(), tolltype, "", gIR->scopebb()); - } - else { - rval = new llvm::FPToSIInst(val->getRVal(), tolltype, "", gIR->scopebb()); - } - } - else { - error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); - fatal(); - } - - return new DImValue(to, rval); -} - -DValue* DtoCastDelegate(Loc& loc, DValue* val, Type* to) -{ - if (to->toBasetype()->ty == Tdelegate) - { - return DtoPaintType(loc, val, to); - } - else if (to->toBasetype()->ty == Tbool) - { - return new DImValue(to, DtoDelegateEquals(TOKnotequal, val->getRVal(), NULL)); - } - else - { - error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); - fatal(); - } -} - -DValue* DtoCastVector(Loc& loc, DValue* val, Type* to) -{ - assert(val->getType()->toBasetype()->ty == Tvector); - Type* totype = to->toBasetype(); - LLType* tolltype = DtoType(to); - TypeVector *type = static_cast(val->getType()->toBasetype()); - - if (totype->ty == Tsarray) - { - // If possible, we need to cast only the address of the vector without - // creating a copy, because, besides the fact that this seem to be the - // language semantics, DMD rewrites e.g. float4.array to - // cast(float[4])array. - if (val->isLVal()) - { - LLValue* vector = val->getLVal(); - IF_LOG Logger::cout() << "src: " << *vector << "to type: " - << *tolltype << " (casting address)\n"; - return new DVarValue(to, DtoBitCast(vector, getPtrToType(tolltype))); - } - else - { - LLValue* vector = val->getRVal(); - IF_LOG Logger::cout() << "src: " << *vector << "to type: " - << *tolltype << " (creating temporary)\n"; - LLValue *array = DtoAlloca(to); - - TypeSArray *st = static_cast(totype); - - for (int i = 0, n = st->dim->toInteger(); i < n; ++i) { - LLValue *lelem = DtoExtractElement(vector, i); - DImValue elem(type->elementType(), lelem); - lelem = DtoCast(loc, &elem, to->nextOf())->getRVal(); - DtoStore(lelem, DtoGEPi(array, 0, i)); - } - - return new DImValue(to, array); - } - } - else if (totype->ty == Tvector && to->size() == val->getType()->size()) - { - return new DImValue(to, DtoBitCast(val->getRVal(), tolltype)); - } - else - { - error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); - fatal(); - } -} - -DValue* DtoCast(Loc& loc, DValue* val, Type* to) -{ - Type* fromtype = val->getType()->toBasetype(); - Type* totype = to->toBasetype(); - - if (fromtype->ty == Taarray) - { - // DMD allows casting AAs to void*, even if they are internally - // implemented as structs. - if (totype->ty == Tpointer) - { - IF_LOG Logger::println("Casting AA to pointer."); - LLValue *rval = DtoBitCast(val->getRVal(), DtoType(to)); - return new DImValue(to, rval); - } - else if (totype->ty == Tbool) - { - IF_LOG Logger::println("Casting AA to bool."); - LLValue* rval = val->getRVal(); - LLValue* zero = LLConstant::getNullValue(rval->getType()); - return new DImValue(to, gIR->ir->CreateICmpNE(rval, zero)); - } - } - - if (fromtype->equals(totype)) - return val; - - IF_LOG Logger::println("Casting from '%s' to '%s'", fromtype->toChars(), to->toChars()); - LOG_SCOPE; - - if (fromtype->ty == Tvector) { - return DtoCastVector(loc, val, to); - } - else if (fromtype->isintegral()) { - return DtoCastInt(loc, val, to); - } - else if (fromtype->iscomplex()) { - return DtoCastComplex(loc, val, to); - } - else if (fromtype->isfloating()) { - return DtoCastFloat(loc, val, to); - } - else if (fromtype->ty == Tclass) { - return DtoCastClass(loc, val, to); - } - else if (fromtype->ty == Tarray || fromtype->ty == Tsarray) { - return DtoCastArray(loc, val, to); - } - else if (fromtype->ty == Tpointer || fromtype->ty == Tfunction) { - return DtoCastPtr(loc, val, to); - } - else if (fromtype->ty == Tdelegate) { - return DtoCastDelegate(loc, val, to); - } - else if (fromtype->ty == Tnull) { - return DtoNullValue(to, loc); - } - else if (fromtype->ty == totype->ty) { - return val; + if (to->ty == Tbool) { + LLValue *zero = LLConstantInt::get(rval->getType(), 0, false); + rval = gIR->ir->CreateICmpNE(rval, zero); + } else if (to->isintegral()) { + if (fromsz < tosz || from->ty == Tbool) { + IF_LOG Logger::cout() << "cast to: " << *tolltype << '\n'; + if (isLLVMUnsigned(from) || from->ty == Tbool) { + rval = new llvm::ZExtInst(rval, tolltype, "", gIR->scopebb()); + } else { + rval = new llvm::SExtInst(rval, tolltype, "", gIR->scopebb()); + } + } else if (fromsz > tosz) { + rval = new llvm::TruncInst(rval, tolltype, "", gIR->scopebb()); } else { - error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); - fatal(); + rval = DtoBitCast(rval, tolltype); } + } else if (to->iscomplex()) { + return DtoComplex(loc, to, val); + } else if (to->isfloating()) { + if (from->isunsigned()) { + rval = new llvm::UIToFPInst(rval, tolltype, "", gIR->scopebb()); + } else { + rval = new llvm::SIToFPInst(rval, tolltype, "", gIR->scopebb()); + } + } else if (to->ty == Tpointer) { + IF_LOG Logger::cout() << "cast pointer: " << *tolltype << '\n'; + rval = gIR->ir->CreateIntToPtr(rval, tolltype); + } else { + error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), + _to->toChars()); + fatal(); + } + + return new DImValue(_to, rval); +} + +DValue *DtoCastPtr(Loc &loc, DValue *val, Type *to) { + LLType *tolltype = DtoType(to); + + Type *totype = to->toBasetype(); + Type *fromtype = val->getType()->toBasetype(); + assert(fromtype->ty == Tpointer || fromtype->ty == Tfunction); + + LLValue *rval; + + if (totype->ty == Tpointer || totype->ty == Tclass) { + LLValue *src = val->getRVal(); + IF_LOG { + Logger::cout() << "src: " << *src << '\n'; + Logger::cout() << "to type: " << *tolltype << '\n'; + } + rval = DtoBitCast(src, tolltype); + } else if (totype->ty == Tbool) { + LLValue *src = val->getRVal(); + LLValue *zero = LLConstant::getNullValue(src->getType()); + rval = gIR->ir->CreateICmpNE(src, zero); + } else if (totype->isintegral()) { + rval = new llvm::PtrToIntInst(val->getRVal(), tolltype, "", gIR->scopebb()); + } else { + error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), + to->toChars()); + fatal(); + } + + return new DImValue(to, rval); +} + +DValue *DtoCastFloat(Loc &loc, DValue *val, Type *to) { + if (val->getType() == to) + return val; + + LLType *tolltype = DtoType(to); + + Type *totype = to->toBasetype(); + Type *fromtype = val->getType()->toBasetype(); + assert(fromtype->isfloating()); + + size_t fromsz = fromtype->size(); + size_t tosz = totype->size(); + + LLValue *rval; + + if (totype->ty == Tbool) { + rval = val->getRVal(); + LLValue *zero = LLConstant::getNullValue(rval->getType()); + rval = gIR->ir->CreateFCmpUNE(rval, zero); + } else if (totype->iscomplex()) { + return DtoComplex(loc, to, val); + } else if (totype->isfloating()) { + if (fromsz == tosz) { + rval = val->getRVal(); + assert(rval->getType() == tolltype); + } else if (fromsz < tosz) { + rval = new llvm::FPExtInst(val->getRVal(), tolltype, "", gIR->scopebb()); + } else if (fromsz > tosz) { + rval = + new llvm::FPTruncInst(val->getRVal(), tolltype, "", gIR->scopebb()); + } else { + error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), + to->toChars()); + fatal(); + } + } else if (totype->isintegral()) { + if (totype->isunsigned()) { + rval = new llvm::FPToUIInst(val->getRVal(), tolltype, "", gIR->scopebb()); + } else { + rval = new llvm::FPToSIInst(val->getRVal(), tolltype, "", gIR->scopebb()); + } + } else { + error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), + to->toChars()); + fatal(); + } + + return new DImValue(to, rval); +} + +DValue *DtoCastDelegate(Loc &loc, DValue *val, Type *to) { + if (to->toBasetype()->ty == Tdelegate) { + return DtoPaintType(loc, val, to); + } else if (to->toBasetype()->ty == Tbool) { + return new DImValue(to, + DtoDelegateEquals(TOKnotequal, val->getRVal(), NULL)); + } else { + error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), + to->toChars()); + fatal(); + } +} + +DValue *DtoCastVector(Loc &loc, DValue *val, Type *to) { + assert(val->getType()->toBasetype()->ty == Tvector); + Type *totype = to->toBasetype(); + LLType *tolltype = DtoType(to); + TypeVector *type = static_cast(val->getType()->toBasetype()); + + if (totype->ty == Tsarray) { + // If possible, we need to cast only the address of the vector without + // creating a copy, because, besides the fact that this seem to be the + // language semantics, DMD rewrites e.g. float4.array to + // cast(float[4])array. + if (val->isLVal()) { + LLValue *vector = val->getLVal(); + IF_LOG Logger::cout() << "src: " << *vector << "to type: " << *tolltype + << " (casting address)\n"; + return new DVarValue(to, DtoBitCast(vector, getPtrToType(tolltype))); + } else { + LLValue *vector = val->getRVal(); + IF_LOG Logger::cout() << "src: " << *vector << "to type: " << *tolltype + << " (creating temporary)\n"; + LLValue *array = DtoAlloca(to); + + TypeSArray *st = static_cast(totype); + + for (int i = 0, n = st->dim->toInteger(); i < n; ++i) { + LLValue *lelem = DtoExtractElement(vector, i); + DImValue elem(type->elementType(), lelem); + lelem = DtoCast(loc, &elem, to->nextOf())->getRVal(); + DtoStore(lelem, DtoGEPi(array, 0, i)); + } + + return new DImValue(to, array); + } + } else if (totype->ty == Tvector && to->size() == val->getType()->size()) { + return new DImValue(to, DtoBitCast(val->getRVal(), tolltype)); + } else { + error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), + to->toChars()); + fatal(); + } +} + +DValue *DtoCast(Loc &loc, DValue *val, Type *to) { + Type *fromtype = val->getType()->toBasetype(); + Type *totype = to->toBasetype(); + + if (fromtype->ty == Taarray) { + // DMD allows casting AAs to void*, even if they are internally + // implemented as structs. + if (totype->ty == Tpointer) { + IF_LOG Logger::println("Casting AA to pointer."); + LLValue *rval = DtoBitCast(val->getRVal(), DtoType(to)); + return new DImValue(to, rval); + } else if (totype->ty == Tbool) { + IF_LOG Logger::println("Casting AA to bool."); + LLValue *rval = val->getRVal(); + LLValue *zero = LLConstant::getNullValue(rval->getType()); + return new DImValue(to, gIR->ir->CreateICmpNE(rval, zero)); + } + } + + if (fromtype->equals(totype)) + return val; + + IF_LOG Logger::println("Casting from '%s' to '%s'", fromtype->toChars(), + to->toChars()); + LOG_SCOPE; + + if (fromtype->ty == Tvector) { + return DtoCastVector(loc, val, to); + } else if (fromtype->isintegral()) { + return DtoCastInt(loc, val, to); + } else if (fromtype->iscomplex()) { + return DtoCastComplex(loc, val, to); + } else if (fromtype->isfloating()) { + return DtoCastFloat(loc, val, to); + } else if (fromtype->ty == Tclass) { + return DtoCastClass(loc, val, to); + } else if (fromtype->ty == Tarray || fromtype->ty == Tsarray) { + return DtoCastArray(loc, val, to); + } else if (fromtype->ty == Tpointer || fromtype->ty == Tfunction) { + return DtoCastPtr(loc, val, to); + } else if (fromtype->ty == Tdelegate) { + return DtoCastDelegate(loc, val, to); + } else if (fromtype->ty == Tnull) { + return DtoNullValue(to, loc); + } else if (fromtype->ty == totype->ty) { + return val; + } else { + error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), + to->toChars()); + fatal(); + } } ////////////////////////////////////////////////////////////////////////////////////////// -DValue* DtoPaintType(Loc& loc, DValue* val, Type* to) -{ - Type* from = val->getType()->toBasetype(); - IF_LOG Logger::println("repainting from '%s' to '%s'", from->toChars(), to->toChars()); +DValue *DtoPaintType(Loc &loc, DValue *val, Type *to) { + Type *from = val->getType()->toBasetype(); + IF_LOG Logger::println("repainting from '%s' to '%s'", from->toChars(), + to->toChars()); - if (from->ty == Tarray) - { - Type* at = to->toBasetype(); - assert(at->ty == Tarray); - Type* elem = at->nextOf()->pointerTo(); - if (DSliceValue* slice = val->isSlice()) - { - return new DSliceValue(to, slice->len, DtoBitCast(slice->ptr, DtoType(elem))); - } - else if (val->isLVal()) - { - LLValue* ptr = val->getLVal(); - ptr = DtoBitCast(ptr, DtoType(at->pointerTo())); - return new DVarValue(to, ptr); - } - else - { - LLValue *len, *ptr; - len = DtoArrayLen(val); - ptr = DtoArrayPtr(val); - ptr = DtoBitCast(ptr, DtoType(elem)); - return new DImValue(to, DtoAggrPair(len, ptr)); - } + if (from->ty == Tarray) { + Type *at = to->toBasetype(); + assert(at->ty == Tarray); + Type *elem = at->nextOf()->pointerTo(); + if (DSliceValue *slice = val->isSlice()) { + return new DSliceValue(to, slice->len, + DtoBitCast(slice->ptr, DtoType(elem))); + } else if (val->isLVal()) { + LLValue *ptr = val->getLVal(); + ptr = DtoBitCast(ptr, DtoType(at->pointerTo())); + return new DVarValue(to, ptr); + } else { + LLValue *len, *ptr; + len = DtoArrayLen(val); + ptr = DtoArrayPtr(val); + ptr = DtoBitCast(ptr, DtoType(elem)); + return new DImValue(to, DtoAggrPair(len, ptr)); } - else if (from->ty == Tdelegate) - { - Type* dgty = to->toBasetype(); - assert(dgty->ty == Tdelegate); - if (val->isLVal()) - { - LLValue* ptr = val->getLVal(); - assert(isaPointer(ptr)); - ptr = DtoBitCast(ptr, DtoPtrToType(dgty)); - IF_LOG Logger::cout() << "dg ptr: " << *ptr << '\n'; - return new DVarValue(to, ptr); - } - else - { - LLValue* dg = val->getRVal(); - LLValue* context = gIR->ir->CreateExtractValue(dg, 0, ".context"); - LLValue* funcptr = gIR->ir->CreateExtractValue(dg, 1, ".funcptr"); - funcptr = DtoBitCast(funcptr, DtoType(dgty)->getContainedType(1)); - LLValue* aggr = DtoAggrPair(context, funcptr); - IF_LOG Logger::cout() << "dg: " << *aggr << '\n'; - return new DImValue(to, aggr); - } - } - else if (from->ty == Tpointer || from->ty == Tclass || from->ty == Taarray) - { - Type* b = to->toBasetype(); - assert(b->ty == Tpointer || b->ty == Tclass || b->ty == Taarray); - LLValue* ptr = DtoBitCast(val->getRVal(), DtoType(b)); - return new DImValue(to, ptr); - } - else - { - // assert(!val->isLVal()); TODO: what is it needed for? - assert(DtoType(to) == DtoType(to)); - return new DImValue(to, val->getRVal()); + } else if (from->ty == Tdelegate) { + Type *dgty = to->toBasetype(); + assert(dgty->ty == Tdelegate); + if (val->isLVal()) { + LLValue *ptr = val->getLVal(); + assert(isaPointer(ptr)); + ptr = DtoBitCast(ptr, DtoPtrToType(dgty)); + IF_LOG Logger::cout() << "dg ptr: " << *ptr << '\n'; + return new DVarValue(to, ptr); + } else { + LLValue *dg = val->getRVal(); + LLValue *context = gIR->ir->CreateExtractValue(dg, 0, ".context"); + LLValue *funcptr = gIR->ir->CreateExtractValue(dg, 1, ".funcptr"); + funcptr = DtoBitCast(funcptr, DtoType(dgty)->getContainedType(1)); + LLValue *aggr = DtoAggrPair(context, funcptr); + IF_LOG Logger::cout() << "dg: " << *aggr << '\n'; + return new DImValue(to, aggr); } + } else if (from->ty == Tpointer || from->ty == Tclass || + from->ty == Taarray) { + Type *b = to->toBasetype(); + assert(b->ty == Tpointer || b->ty == Tclass || b->ty == Taarray); + LLValue *ptr = DtoBitCast(val->getRVal(), DtoType(b)); + return new DImValue(to, ptr); + } else { + // assert(!val->isLVal()); TODO: what is it needed for? + assert(DtoType(to) == DtoType(to)); + return new DImValue(to, val->getRVal()); + } } /****************************************************************************************/ @@ -782,14 +706,14 @@ DValue* DtoPaintType(Loc& loc, DValue* val, Type* to) // TEMPLATE HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ -TemplateInstance* DtoIsTemplateInstance(Dsymbol* s) -{ - if (!s) return NULL; - if (s->isTemplateInstance() && !s->isTemplateMixin()) - return s->isTemplateInstance(); - if (s->parent) - return DtoIsTemplateInstance(s->parent); +TemplateInstance *DtoIsTemplateInstance(Dsymbol *s) { + if (!s) return NULL; + if (s->isTemplateInstance() && !s->isTemplateMixin()) + return s->isTemplateInstance(); + if (s->parent) + return DtoIsTemplateInstance(s->parent); + return NULL; } /****************************************************************************************/ @@ -797,105 +721,101 @@ TemplateInstance* DtoIsTemplateInstance(Dsymbol* s) // PROCESSING QUEUE HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ -void DtoResolveDsymbol(Dsymbol* dsym) -{ - if (StructDeclaration* sd = dsym->isStructDeclaration()) { - DtoResolveStruct(sd); - } - else if (ClassDeclaration* cd = dsym->isClassDeclaration()) { - DtoResolveClass(cd); - } - else if (FuncDeclaration* fd = dsym->isFuncDeclaration()) { - DtoResolveFunction(fd); - } - else if (TypeInfoDeclaration* tid = dsym->isTypeInfoDeclaration()) { - DtoResolveTypeInfo(tid); - } - else if (VarDeclaration* vd = dsym->isVarDeclaration()) { - DtoResolveVariable(vd); - } +void DtoResolveDsymbol(Dsymbol *dsym) { + if (StructDeclaration *sd = dsym->isStructDeclaration()) { + DtoResolveStruct(sd); + } else if (ClassDeclaration *cd = dsym->isClassDeclaration()) { + DtoResolveClass(cd); + } else if (FuncDeclaration *fd = dsym->isFuncDeclaration()) { + DtoResolveFunction(fd); + } else if (TypeInfoDeclaration *tid = dsym->isTypeInfoDeclaration()) { + DtoResolveTypeInfo(tid); + } else if (VarDeclaration *vd = dsym->isVarDeclaration()) { + DtoResolveVariable(vd); + } } -void DtoResolveVariable(VarDeclaration* vd) -{ - if (vd->isTypeInfoDeclaration()) - return DtoResolveTypeInfo(static_cast(vd)); +void DtoResolveVariable(VarDeclaration *vd) { + if (vd->isTypeInfoDeclaration()) + return DtoResolveTypeInfo(static_cast(vd)); - IF_LOG Logger::println("DtoResolveVariable(%s)", vd->toPrettyChars()); - LOG_SCOPE; + IF_LOG Logger::println("DtoResolveVariable(%s)", vd->toPrettyChars()); + LOG_SCOPE; - // just forward aliases - // TODO: Is this required here or is the check in VarDeclaration::codegen - // sufficient? - if (vd->aliassym) - { - Logger::println("alias sym"); - DtoResolveDsymbol(vd->aliassym); - return; + // just forward aliases + // TODO: Is this required here or is the check in VarDeclaration::codegen + // sufficient? + if (vd->aliassym) { + Logger::println("alias sym"); + DtoResolveDsymbol(vd->aliassym); + return; + } + + if (AggregateDeclaration *ad = vd->isMember()) + DtoResolveDsymbol(ad); + + // global variable + if (vd->isDataseg()) { + Logger::println("data segment"); + + assert(!(vd->storage_class & STCmanifest) && + "manifest constant being codegen'd!"); + + // don't duplicate work + if (vd->ir.isResolved()) + return; + vd->ir.setDeclared(); + + getIrGlobal(vd, true); + + IF_LOG { + if (vd->parent) + Logger::println("parent: %s (%s)", vd->parent->toChars(), + vd->parent->kind()); + else + Logger::println("parent: null"); } - if (AggregateDeclaration* ad = vd->isMember()) - DtoResolveDsymbol(ad); + // If a const/immutable value has a proper initializer (not "= void"), + // it cannot be assigned again in a static constructor. Thus, we can + // emit it as read-only data. + const bool isLLConst = (vd->isConst() || vd->isImmutable()) && vd->init && + !vd->init->isVoidInitializer(); - // global variable - if (vd->isDataseg()) - { - Logger::println("data segment"); + assert(!vd->ir.isInitialized()); + if (gIR->dmodule) + vd->ir.setInitialized(); + std::string llName(mangle(vd)); - assert(!(vd->storage_class & STCmanifest) && - "manifest constant being codegen'd!"); + // Since the type of a global must exactly match the type of its + // initializer, we cannot know the type until after we have emitted the + // latter (e.g. in case of unions, …). However, it is legal for the + // initializer to refer to the address of the variable. Thus, we first + // create a global with the generic type (note the assignment to + // vd->ir.irGlobal->value!), and in case we also do an initializer + // with a different type later, swap it out and replace any existing + // uses with bitcasts to the previous type. - // don't duplicate work - if (vd->ir.isResolved()) return; - vd->ir.setDeclared(); - - getIrGlobal(vd, true); - - IF_LOG { - if (vd->parent) - Logger::println("parent: %s (%s)", vd->parent->toChars(), vd->parent->kind()); - else - Logger::println("parent: null"); - } - - // If a const/immutable value has a proper initializer (not "= void"), - // it cannot be assigned again in a static constructor. Thus, we can - // emit it as read-only data. - const bool isLLConst = (vd->isConst() || vd->isImmutable()) && - vd->init && !vd->init->isVoidInitializer(); - - assert(!vd->ir.isInitialized()); - if (gIR->dmodule) - vd->ir.setInitialized(); - std::string llName(mangle(vd)); - - // Since the type of a global must exactly match the type of its - // initializer, we cannot know the type until after we have emitted the - // latter (e.g. in case of unions, …). However, it is legal for the - // initializer to refer to the address of the variable. Thus, we first - // create a global with the generic type (note the assignment to - // vd->ir.irGlobal->value!), and in case we also do an initializer - // with a different type later, swap it out and replace any existing - // uses with bitcasts to the previous type. - - // We always start out with external linkage; any other type is set - // when actually defining it in VarDeclaration::codegen. - llvm::GlobalValue::LinkageTypes linkage = llvm::GlobalValue::ExternalLinkage; - if (vd->llvmInternal == LLVMextern_weak) { - linkage = llvm::GlobalValue::ExternalWeakLinkage; - } - - llvm::GlobalVariable* gvar = getOrCreateGlobal(vd->loc, gIR->module, - DtoMemType(vd->type), isLLConst, linkage, 0, llName, - vd->isThreadlocal()); - getIrGlobal(vd)->value = gvar; - - // Set the alignment and use the target pointer size as lower bound. - unsigned alignment = std::max(DtoAlignment(vd), gDataLayout->getPointerSize()); - gvar->setAlignment(alignment); - - IF_LOG Logger::cout() << *gvar << '\n'; + // We always start out with external linkage; any other type is set + // when actually defining it in VarDeclaration::codegen. + llvm::GlobalValue::LinkageTypes linkage = + llvm::GlobalValue::ExternalLinkage; + if (vd->llvmInternal == LLVMextern_weak) { + linkage = llvm::GlobalValue::ExternalWeakLinkage; } + + llvm::GlobalVariable *gvar = + getOrCreateGlobal(vd->loc, gIR->module, DtoMemType(vd->type), isLLConst, + linkage, 0, llName, vd->isThreadlocal()); + getIrGlobal(vd)->value = gvar; + + // Set the alignment and use the target pointer size as lower bound. + unsigned alignment = + std::max(DtoAlignment(vd), gDataLayout->getPointerSize()); + gvar->setAlignment(alignment); + + IF_LOG Logger::cout() << *gvar << '\n'; + } } /****************************************************************************************/ @@ -904,276 +824,251 @@ void DtoResolveVariable(VarDeclaration* vd) ////////////////////////////////////////////////////////////////////////////////////////*/ // TODO: Merge with DtoRawVarDeclaration! -void DtoVarDeclaration(VarDeclaration* vd) -{ - assert(!vd->isDataseg() && "Statics/globals are handled in DtoDeclarationExp."); - assert(!vd->aliassym && "Aliases are handled in DtoDeclarationExp."); +void DtoVarDeclaration(VarDeclaration *vd) { + assert(!vd->isDataseg() && + "Statics/globals are handled in DtoDeclarationExp."); + assert(!vd->aliassym && "Aliases are handled in DtoDeclarationExp."); - IF_LOG Logger::println("DtoVarDeclaration(vdtype = %s)", vd->type->toChars()); - LOG_SCOPE + IF_LOG Logger::println("DtoVarDeclaration(vdtype = %s)", vd->type->toChars()); + LOG_SCOPE - if (vd->nestedrefs.dim) - { - IF_LOG Logger::println("has nestedref set (referenced by nested function/delegate)"); + if (vd->nestedrefs.dim) { + IF_LOG Logger::println( + "has nestedref set (referenced by nested function/delegate)"); - // A variable may not be really nested even if nextedrefs is not empty - // in case it is referenced by a function inside __traits(compile) or typeof. - // assert(vd->ir.irLocal && "irLocal is expected to be already set by DtoCreateNestedContext"); - } - - if (isIrLocalCreated(vd)) - { - // Nothing to do if it has already been allocated. - } - /* Named Return Value Optimization (NRVO): - T f(){ - T ret; // &ret == hidden pointer - ret = ... - return ret; // NRVO. - } - */ - else if (gIR->func()->retArg && gIR->func()->decl->nrvo_can && gIR->func()->decl->nrvo_var == vd) { - assert(!isSpecialRefVar(vd) && "Can this happen?"); - IrLocal *irLocal = getIrLocal(vd, true); - irLocal->value = gIR->func()->retArg; - } - // normal stack variable, allocate storage on the stack if it has not already been done - else { - IrLocal *irLocal = getIrLocal(vd, true); - - Type* type = isSpecialRefVar(vd) ? vd->type->pointerTo() : vd->type; - - llvm::Value* allocainst; - LLType* lltype = DtoType(type); - if(gDataLayout->getTypeSizeInBits(lltype) == 0) - allocainst = llvm::ConstantPointerNull::get(getPtrToType(lltype)); - else if (type != vd->type) - allocainst = DtoAlloca(type, vd->toChars()); - else - allocainst = DtoAlloca(vd, vd->toChars()); - - irLocal->value = allocainst; - - gIR->DBuilder.EmitLocalVariable(allocainst, vd); - - /* NRVO again: - T t = f(); // t's memory address is taken hidden pointer - */ - Type *vdBasetype = vd->type->toBasetype(); - ExpInitializer *ei = 0; - if ((vdBasetype->ty == Tstruct || vdBasetype->ty == Tsarray) && - vd->init && - (ei = vd->init->isExpInitializer())) - { - if (ei->exp->op == TOKconstruct) { - AssignExp *ae = static_cast(ei->exp); - Expression *rhs = ae->e2; - - // Allow casts only emitted because of differing static array - // constness. See runnable.sdtor.test10094. - if (rhs->op == TOKcast && vdBasetype->ty == Tsarray) { - Expression *castSource = ((CastExp *)rhs)->e1; - Type *rhsElem = castSource->type->toBasetype()->nextOf(); - if (rhsElem) { - Type *l = vdBasetype->nextOf()->arrayOf()->immutableOf(); - Type *r = rhsElem->arrayOf()->immutableOf(); - if (l->equals(r)) { - rhs = castSource; - } - } - } - - if (rhs->op == TOKcall) { - CallExp *ce = static_cast(rhs); - if (DtoIsReturnInArg(ce)) - { - if (isSpecialRefVar(vd)) - { - LLValue* const val = toElem(ce)->getLVal(); - DtoStore(val, irLocal->value); - } - else - { - DValue* fnval = toElem(ce->e1); - DtoCallFunction(ce->loc, ce->type, fnval, ce->arguments, irLocal->value); - } - return; - } - } - } - } - } - - IF_LOG Logger::cout() << "llvm value for decl: " << *getIrLocal(vd)->value << '\n'; - - if (vd->init) - { - if (ExpInitializer* ex = vd->init->isExpInitializer()) - { - // TODO: Refactor this so that it doesn't look like toElem has no effect. - Logger::println("expression initializer"); - toElem(ex->exp); - } - } -} - -DValue* DtoDeclarationExp(Dsymbol* declaration) -{ - IF_LOG Logger::print("DtoDeclarationExp: %s\n", declaration->toChars()); - LOG_SCOPE; - - // variable declaration - if (VarDeclaration* vd = declaration->isVarDeclaration()) - { - Logger::println("VarDeclaration"); - - // if aliassym is set, this VarDecl is redone as an alias to another symbol - // this seems to be done to rewrite Tuple!(...) v; - // as a TupleDecl that contains a bunch of individual VarDecls - if (vd->aliassym) - return DtoDeclarationExp(vd->aliassym); - - if (vd->storage_class & STCmanifest) - { - IF_LOG Logger::println("Manifest constant, nothing to do."); - return 0; - } - - // static - if (vd->isDataseg()) - { - Declaration_codegen(vd); - } - else - { - DtoVarDeclaration(vd); - } - return new DVarValue(vd->type, vd, getIrValue(vd)); - } - // struct declaration - else if (StructDeclaration* s = declaration->isStructDeclaration()) - { - Logger::println("StructDeclaration"); - Declaration_codegen(s); - } - // function declaration - else if (FuncDeclaration* f = declaration->isFuncDeclaration()) - { - Logger::println("FuncDeclaration"); - Declaration_codegen(f); - } - // class - else if (ClassDeclaration* e = declaration->isClassDeclaration()) - { - Logger::println("ClassDeclaration"); - Declaration_codegen(e); - } - // attribute declaration - else if (AttribDeclaration* a = declaration->isAttribDeclaration()) - { - Logger::println("AttribDeclaration"); - // choose the right set in case this is a conditional declaration - Dsymbols *d = a->include(NULL, NULL); - if (d) - for (unsigned i=0; i < d->dim; ++i) - { - DtoDeclarationExp((*d)[i]); - } - } - // mixin declaration - else if (TemplateMixin* m = declaration->isTemplateMixin()) - { - Logger::println("TemplateMixin"); - for (unsigned i=0; i < m->members->dim; ++i) - { - Dsymbol* mdsym = static_cast(m->members->data[i]); - DtoDeclarationExp(mdsym); - } - } - // tuple declaration - else if (TupleDeclaration* tupled = declaration->isTupleDeclaration()) - { - Logger::println("TupleDeclaration"); - assert(tupled->isexp && "Non-expression tuple decls not handled yet."); - assert(tupled->objects); - for (unsigned i=0; i < tupled->objects->dim; ++i) - { - DsymbolExp* exp = static_cast(tupled->objects->data[i]); - DtoDeclarationExp(exp->s); - } - } - else - { - // Do nothing for template/alias/enum declarations and static - // assertions. We cannot detect StaticAssert without RTTI, so don't - // even bother to check. - IF_LOG Logger::println("Ignoring Symbol: %s", declaration->kind()); - } - - return 0; -} - -// does pretty much the same as DtoDeclarationExp, except it doesn't initialize, and only handles var declarations -LLValue* DtoRawVarDeclaration(VarDeclaration* var, LLValue* addr) -{ - // we don't handle globals with this one - assert(!var->isDataseg()); - - // we don't handle aliases either - assert(!var->aliassym); - - IrLocal *irLocal = isIrLocalCreated(var) ? getIrLocal(var) : 0; - - // alloca if necessary - if (!addr && (!irLocal || !irLocal->value)) - { - addr = DtoAlloca(var, var->toChars()); - // add debug info - if (!irLocal) - irLocal = getIrLocal(var, true); - gIR->DBuilder.EmitLocalVariable(addr, var); - } - - // nested variable? // A variable may not be really nested even if nextedrefs is not empty - // in case it is referenced by a function inside __traits(compile) or typeof. - if (var->nestedrefs.dim && isIrLocalCreated(var)) - { - if (!irLocal->value) - { - assert(addr); - irLocal->value = addr; - } - else - assert(!addr || addr == irLocal->value); - } - // normal local variable + // in case it is referenced by a function inside __traits(compile) or + // typeof. + // assert(vd->ir.irLocal && "irLocal is expected to be already set by + // DtoCreateNestedContext"); + } + + if (isIrLocalCreated(vd)) { + // Nothing to do if it has already been allocated. + } + /* Named Return Value Optimization (NRVO): + T f(){ + T ret; // &ret == hidden pointer + ret = ... + return ret; // NRVO. + } + */ + else if (gIR->func()->retArg && gIR->func()->decl->nrvo_can && + gIR->func()->decl->nrvo_var == vd) { + assert(!isSpecialRefVar(vd) && "Can this happen?"); + IrLocal *irLocal = getIrLocal(vd, true); + irLocal->value = gIR->func()->retArg; + } + // normal stack variable, allocate storage on the stack if it has not already + // been done + else { + IrLocal *irLocal = getIrLocal(vd, true); + + Type *type = isSpecialRefVar(vd) ? vd->type->pointerTo() : vd->type; + + llvm::Value *allocainst; + LLType *lltype = DtoType(type); + if (gDataLayout->getTypeSizeInBits(lltype) == 0) + allocainst = llvm::ConstantPointerNull::get(getPtrToType(lltype)); + else if (type != vd->type) + allocainst = DtoAlloca(type, vd->toChars()); else - { - // if this already has storage, it must've been handled already - if (irLocal->value) { - if (addr && addr != irLocal->value) { - // This can happen, for example, in scope(exit) blocks which - // are translated to IR multiple times. - // That *should* only happen after the first one is completely done - // though, so just set the address. - IF_LOG { - Logger::println("Replacing LLVM address of %s", var->toChars()); - LOG_SCOPE; - Logger::cout() << "Old val: " << *irLocal->value << '\n'; - Logger::cout() << "New val: " << *addr << '\n'; - } - irLocal->value = addr; + allocainst = DtoAlloca(vd, vd->toChars()); + + irLocal->value = allocainst; + + gIR->DBuilder.EmitLocalVariable(allocainst, vd); + + /* NRVO again: + T t = f(); // t's memory address is taken hidden pointer + */ + Type *vdBasetype = vd->type->toBasetype(); + ExpInitializer *ei = 0; + if ((vdBasetype->ty == Tstruct || vdBasetype->ty == Tsarray) && vd->init && + (ei = vd->init->isExpInitializer())) { + if (ei->exp->op == TOKconstruct) { + AssignExp *ae = static_cast(ei->exp); + Expression *rhs = ae->e2; + + // Allow casts only emitted because of differing static array + // constness. See runnable.sdtor.test10094. + if (rhs->op == TOKcast && vdBasetype->ty == Tsarray) { + Expression *castSource = ((CastExp *)rhs)->e1; + Type *rhsElem = castSource->type->toBasetype()->nextOf(); + if (rhsElem) { + Type *l = vdBasetype->nextOf()->arrayOf()->immutableOf(); + Type *r = rhsElem->arrayOf()->immutableOf(); + if (l->equals(r)) { + rhs = castSource; } - return addr; + } } - assert(addr); - irLocal->value = addr; + if (rhs->op == TOKcall) { + CallExp *ce = static_cast(rhs); + if (DtoIsReturnInArg(ce)) { + if (isSpecialRefVar(vd)) { + LLValue *const val = toElem(ce)->getLVal(); + DtoStore(val, irLocal->value); + } else { + DValue *fnval = toElem(ce->e1); + DtoCallFunction(ce->loc, ce->type, fnval, ce->arguments, + irLocal->value); + } + return; + } + } + } + } + } + + IF_LOG Logger::cout() << "llvm value for decl: " << *getIrLocal(vd)->value + << '\n'; + + if (vd->init) { + if (ExpInitializer *ex = vd->init->isExpInitializer()) { + // TODO: Refactor this so that it doesn't look like toElem has no effect. + Logger::println("expression initializer"); + toElem(ex->exp); + } + } +} + +DValue *DtoDeclarationExp(Dsymbol *declaration) { + IF_LOG Logger::print("DtoDeclarationExp: %s\n", declaration->toChars()); + LOG_SCOPE; + + // variable declaration + if (VarDeclaration *vd = declaration->isVarDeclaration()) { + Logger::println("VarDeclaration"); + + // if aliassym is set, this VarDecl is redone as an alias to another symbol + // this seems to be done to rewrite Tuple!(...) v; + // as a TupleDecl that contains a bunch of individual VarDecls + if (vd->aliassym) + return DtoDeclarationExp(vd->aliassym); + + if (vd->storage_class & STCmanifest) { + IF_LOG Logger::println("Manifest constant, nothing to do."); + return 0; } - // return the alloca - return irLocal->value; + // static + if (vd->isDataseg()) { + Declaration_codegen(vd); + } else { + DtoVarDeclaration(vd); + } + return new DVarValue(vd->type, vd, getIrValue(vd)); + } + // struct declaration + else if (StructDeclaration *s = declaration->isStructDeclaration()) { + Logger::println("StructDeclaration"); + Declaration_codegen(s); + } + // function declaration + else if (FuncDeclaration *f = declaration->isFuncDeclaration()) { + Logger::println("FuncDeclaration"); + Declaration_codegen(f); + } + // class + else if (ClassDeclaration *e = declaration->isClassDeclaration()) { + Logger::println("ClassDeclaration"); + Declaration_codegen(e); + } + // attribute declaration + else if (AttribDeclaration *a = declaration->isAttribDeclaration()) { + Logger::println("AttribDeclaration"); + // choose the right set in case this is a conditional declaration + Dsymbols *d = a->include(NULL, NULL); + if (d) + for (unsigned i = 0; i < d->dim; ++i) { + DtoDeclarationExp((*d)[i]); + } + } + // mixin declaration + else if (TemplateMixin *m = declaration->isTemplateMixin()) { + Logger::println("TemplateMixin"); + for (unsigned i = 0; i < m->members->dim; ++i) { + Dsymbol *mdsym = static_cast(m->members->data[i]); + DtoDeclarationExp(mdsym); + } + } + // tuple declaration + else if (TupleDeclaration *tupled = declaration->isTupleDeclaration()) { + Logger::println("TupleDeclaration"); + assert(tupled->isexp && "Non-expression tuple decls not handled yet."); + assert(tupled->objects); + for (unsigned i = 0; i < tupled->objects->dim; ++i) { + DsymbolExp *exp = static_cast(tupled->objects->data[i]); + DtoDeclarationExp(exp->s); + } + } else { + // Do nothing for template/alias/enum declarations and static + // assertions. We cannot detect StaticAssert without RTTI, so don't + // even bother to check. + IF_LOG Logger::println("Ignoring Symbol: %s", declaration->kind()); + } + + return 0; +} + +// does pretty much the same as DtoDeclarationExp, except it doesn't initialize, +// and only handles var declarations +LLValue *DtoRawVarDeclaration(VarDeclaration *var, LLValue *addr) { + // we don't handle globals with this one + assert(!var->isDataseg()); + + // we don't handle aliases either + assert(!var->aliassym); + + IrLocal *irLocal = isIrLocalCreated(var) ? getIrLocal(var) : 0; + + // alloca if necessary + if (!addr && (!irLocal || !irLocal->value)) { + addr = DtoAlloca(var, var->toChars()); + // add debug info + if (!irLocal) + irLocal = getIrLocal(var, true); + gIR->DBuilder.EmitLocalVariable(addr, var); + } + + // nested variable? + // A variable may not be really nested even if nextedrefs is not empty + // in case it is referenced by a function inside __traits(compile) or typeof. + if (var->nestedrefs.dim && isIrLocalCreated(var)) { + if (!irLocal->value) { + assert(addr); + irLocal->value = addr; + } else + assert(!addr || addr == irLocal->value); + } + // normal local variable + else { + // if this already has storage, it must've been handled already + if (irLocal->value) { + if (addr && addr != irLocal->value) { + // This can happen, for example, in scope(exit) blocks which + // are translated to IR multiple times. + // That *should* only happen after the first one is completely done + // though, so just set the address. + IF_LOG { + Logger::println("Replacing LLVM address of %s", var->toChars()); + LOG_SCOPE; + Logger::cout() << "Old val: " << *irLocal->value << '\n'; + Logger::cout() << "New val: " << *addr << '\n'; + } + irLocal->value = addr; + } + return addr; + } + + assert(addr); + irLocal->value = addr; + } + + // return the alloca + return irLocal->value; } /****************************************************************************************/ @@ -1181,715 +1076,661 @@ LLValue* DtoRawVarDeclaration(VarDeclaration* var, LLValue* addr) // INITIALIZER HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ -LLConstant* DtoConstInitializer(Loc& loc, Type* type, Initializer* init) -{ - LLConstant* _init = 0; // may return zero - if (!init) - { - IF_LOG Logger::println("const default initializer for %s", type->toChars()); - Expression *initExp = type->defaultInit(); - _init = DtoConstExpInit(loc, type, initExp); - } - else if (ExpInitializer* ex = init->isExpInitializer()) - { - Logger::println("const expression initializer"); - _init = DtoConstExpInit(loc, type, ex->exp); - } - else if (ArrayInitializer* ai = init->isArrayInitializer()) - { - Logger::println("const array initializer"); - _init = DtoConstArrayInitializer(ai, type); - } - else if (init->isVoidInitializer()) - { - Logger::println("const void initializer"); - LLType* ty = DtoMemType(type); - _init = LLConstant::getNullValue(ty); - } - else - { - // StructInitializer is no longer suposed to make it to the glue layer - // in DMD 2.064. - IF_LOG Logger::println("unsupported const initializer: %s", init->toChars()); - } - return _init; +LLConstant *DtoConstInitializer(Loc &loc, Type *type, Initializer *init) { + LLConstant *_init = 0; // may return zero + if (!init) { + IF_LOG Logger::println("const default initializer for %s", type->toChars()); + Expression *initExp = type->defaultInit(); + _init = DtoConstExpInit(loc, type, initExp); + } else if (ExpInitializer *ex = init->isExpInitializer()) { + Logger::println("const expression initializer"); + _init = DtoConstExpInit(loc, type, ex->exp); + } else if (ArrayInitializer *ai = init->isArrayInitializer()) { + Logger::println("const array initializer"); + _init = DtoConstArrayInitializer(ai, type); + } else if (init->isVoidInitializer()) { + Logger::println("const void initializer"); + LLType *ty = DtoMemType(type); + _init = LLConstant::getNullValue(ty); + } else { + // StructInitializer is no longer suposed to make it to the glue layer + // in DMD 2.064. + IF_LOG Logger::println("unsupported const initializer: %s", + init->toChars()); + } + return _init; } ////////////////////////////////////////////////////////////////////////////////////////// -LLConstant* DtoConstExpInit(Loc& loc, Type* targetType, Expression* exp) -{ - IF_LOG Logger::println("DtoConstExpInit(targetType = %s, exp = %s)", - targetType->toChars(), exp->toChars()); - LOG_SCOPE +LLConstant *DtoConstExpInit(Loc &loc, Type *targetType, Expression *exp) { + IF_LOG Logger::println("DtoConstExpInit(targetType = %s, exp = %s)", + targetType->toChars(), exp->toChars()); + LOG_SCOPE - LLConstant* val = toConstElem(exp, gIR); + LLConstant *val = toConstElem(exp, gIR); - // The situation here is a bit tricky: In an ideal world, we would always - // have val->getType() == DtoType(targetType). But there are two reasons - // why this is not true. One is that the LLVM type system cannot represent - // all the C types, leading to differences in types being necessary e.g. for - // union initializers. The second is that the frontend actually does not - // explicitly lowers things like initializing an array/vector with a scalar - // constant, or since 2.061 sometimes does not get implicit conversions for - // integers right. However, we cannot just rely on the actual Types being - // equal if there are no rewrites to do because of – as usual – AST - // inconsistency bugs. + // The situation here is a bit tricky: In an ideal world, we would always + // have val->getType() == DtoType(targetType). But there are two reasons + // why this is not true. One is that the LLVM type system cannot represent + // all the C types, leading to differences in types being necessary e.g. for + // union initializers. The second is that the frontend actually does not + // explicitly lowers things like initializing an array/vector with a scalar + // constant, or since 2.061 sometimes does not get implicit conversions for + // integers right. However, we cannot just rely on the actual Types being + // equal if there are no rewrites to do because of – as usual – AST + // inconsistency bugs. - Type* expBase = stripModifiers(exp->type->toBasetype())->merge(); - Type* targetBase = stripModifiers(targetType->toBasetype())->merge(); + Type *expBase = stripModifiers(exp->type->toBasetype())->merge(); + Type *targetBase = stripModifiers(targetType->toBasetype())->merge(); - if (expBase->equals(targetBase) && targetBase->ty != Tbool) - { - return val; - } - - llvm::Type* llType = val->getType(); - llvm::Type* targetLLType = DtoMemType(targetBase); - if (llType == targetLLType) - { - Logger::println("Matching LLVM types, ignoring frontend glitch."); - return val; - } - - if (targetBase->ty == Tsarray) - { - Logger::println("Building constant array initializer to single value."); - - assert(expBase->size() > 0); - d_uns64 elemCount = targetBase->size() / expBase->size(); - assert(targetBase->size() % expBase->size() == 0); - - std::vector initVals(elemCount, val); - return llvm::ConstantArray::get(llvm::ArrayType::get(llType, elemCount), initVals); - } - - if (targetBase->ty == Tvector) - { - Logger::println("Building vector initializer from scalar."); - - TypeVector* tv = static_cast(targetBase); - assert(tv->basetype->ty == Tsarray); - dinteger_t elemCount = - static_cast(tv->basetype)->dim->toInteger(); - return llvm::ConstantVector::getSplat(elemCount, val); - } - - if (llType->isIntegerTy() && targetLLType->isIntegerTy()) - { - // This should really be fixed in the frontend. - Logger::println("Fixing up unresolved implicit integer conversion."); - - llvm::IntegerType* source = llvm::cast(llType); - llvm::IntegerType* target = llvm::cast(targetLLType); - - assert(target->getBitWidth() > source->getBitWidth() && "On initializer " - "integer type mismatch, the target should be wider than the source."); - return llvm::ConstantExpr::getZExtOrBitCast(val, target); - } - - Logger::println("Unhandled type mismatch, giving up."); + if (expBase->equals(targetBase) && targetBase->ty != Tbool) { return val; + } + + llvm::Type *llType = val->getType(); + llvm::Type *targetLLType = DtoMemType(targetBase); + if (llType == targetLLType) { + Logger::println("Matching LLVM types, ignoring frontend glitch."); + return val; + } + + if (targetBase->ty == Tsarray) { + Logger::println("Building constant array initializer to single value."); + + assert(expBase->size() > 0); + d_uns64 elemCount = targetBase->size() / expBase->size(); + assert(targetBase->size() % expBase->size() == 0); + + std::vector initVals(elemCount, val); + return llvm::ConstantArray::get(llvm::ArrayType::get(llType, elemCount), + initVals); + } + + if (targetBase->ty == Tvector) { + Logger::println("Building vector initializer from scalar."); + + TypeVector *tv = static_cast(targetBase); + assert(tv->basetype->ty == Tsarray); + dinteger_t elemCount = + static_cast(tv->basetype)->dim->toInteger(); + return llvm::ConstantVector::getSplat(elemCount, val); + } + + if (llType->isIntegerTy() && targetLLType->isIntegerTy()) { + // This should really be fixed in the frontend. + Logger::println("Fixing up unresolved implicit integer conversion."); + + llvm::IntegerType *source = llvm::cast(llType); + llvm::IntegerType *target = llvm::cast(targetLLType); + + assert( + target->getBitWidth() > source->getBitWidth() && + "On initializer " + "integer type mismatch, the target should be wider than the source."); + return llvm::ConstantExpr::getZExtOrBitCast(val, target); + } + + Logger::println("Unhandled type mismatch, giving up."); + return val; } ////////////////////////////////////////////////////////////////////////////////////////// -LLConstant* DtoTypeInfoOf(Type* type, bool base) -{ - IF_LOG Logger::println("DtoTypeInfoOf(type = '%s', base='%d')", type->toChars(), base); - LOG_SCOPE +LLConstant *DtoTypeInfoOf(Type *type, bool base) { + IF_LOG Logger::println("DtoTypeInfoOf(type = '%s', base='%d')", + type->toChars(), base); + LOG_SCOPE - type = type->merge2(); // needed.. getTypeInfo does the same - getTypeInfoType(type, NULL); - TypeInfoDeclaration* tidecl = type->vtinfo; - assert(tidecl); - Declaration_codegen(tidecl); - assert(getIrGlobal(tidecl)->value != NULL); - LLConstant* c = isaConstant(getIrGlobal(tidecl)->value); - assert(c != NULL); - if (base) - return llvm::ConstantExpr::getBitCast(c, DtoType(Type::dtypeinfo->type)); - return c; + type = type->merge2(); // needed.. getTypeInfo does the same + getTypeInfoType(type, NULL); + TypeInfoDeclaration *tidecl = type->vtinfo; + assert(tidecl); + Declaration_codegen(tidecl); + assert(getIrGlobal(tidecl)->value != NULL); + LLConstant *c = isaConstant(getIrGlobal(tidecl)->value); + assert(c != NULL); + if (base) + return llvm::ConstantExpr::getBitCast(c, DtoType(Type::dtypeinfo->type)); + return c; } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoOverloadedIntrinsicName(TemplateInstance* ti, TemplateDeclaration* td, std::string& name) -{ - IF_LOG Logger::println("DtoOverloadedIntrinsicName"); - LOG_SCOPE; +void DtoOverloadedIntrinsicName(TemplateInstance *ti, TemplateDeclaration *td, + std::string &name) { + IF_LOG Logger::println("DtoOverloadedIntrinsicName"); + LOG_SCOPE; - IF_LOG { - Logger::println("template instance: %s", ti->toChars()); - Logger::println("template declaration: %s", td->toChars()); - Logger::println("intrinsic name: %s", td->intrinsicName.c_str()); + IF_LOG { + Logger::println("template instance: %s", ti->toChars()); + Logger::println("template declaration: %s", td->toChars()); + Logger::println("intrinsic name: %s", td->intrinsicName.c_str()); + } + + // for now use the size in bits of the first template param in the instance + assert(ti->tdtypes.dim == 1); + Type *T = static_cast(ti->tdtypes.data[0]); + + char prefix = T->isreal() ? 'f' : T->isintegral() ? 'i' : 0; + if (!prefix) { + ti->error("has invalid template parameter for intrinsic: %s", T->toChars()); + fatal(); // or LLVM asserts + } + + llvm::Type *dtype(DtoType(T)); + char tmp[21]; // probably excessive, but covers a uint64_t + sprintf(tmp, "%lu", + static_cast(gDataLayout->getTypeSizeInBits(dtype))); + + // replace # in name with bitsize + name = td->intrinsicName; + + std::string needle("#"); + size_t pos; + while (std::string::npos != (pos = name.find(needle))) { + if (pos > 0 && name[pos - 1] == prefix) { + // Check for special PPC128 double + if (dtype->isPPC_FP128Ty()) { + name.insert(pos - 1, "ppc"); + pos += 3; + } + // Properly prefixed, insert bitwidth. + name.replace(pos, 1, tmp); + } else { + if (pos && (name[pos - 1] == 'i' || name[pos - 1] == 'f')) { + // Wrong type character. + ti->error( + "has invalid parameter type for intrinsic %s: %s is not a%s type", + name.c_str(), T->toChars(), + (name[pos - 1] == 'i' ? "n integral" : " floating-point")); + } else { + // Just plain wrong. (Error in declaration, not instantiation) + td->error("has an invalid intrinsic name: %s", name.c_str()); + } + fatal(); // or LLVM asserts } + } - // for now use the size in bits of the first template param in the instance - assert(ti->tdtypes.dim == 1); - Type* T = static_cast(ti->tdtypes.data[0]); - - char prefix = T->isreal() ? 'f' : T->isintegral() ? 'i' : 0; - if (!prefix) { - ti->error("has invalid template parameter for intrinsic: %s", T->toChars()); - fatal(); // or LLVM asserts - } - - llvm::Type *dtype(DtoType(T)); - char tmp[21]; // probably excessive, but covers a uint64_t - sprintf(tmp, "%lu", static_cast(gDataLayout->getTypeSizeInBits(dtype))); - - // replace # in name with bitsize - name = td->intrinsicName; - - std::string needle("#"); - size_t pos; - while(std::string::npos != (pos = name.find(needle))) { - if (pos > 0 && name[pos-1] == prefix) { - // Check for special PPC128 double - if (dtype->isPPC_FP128Ty()) { - name.insert(pos-1, "ppc"); - pos += 3; - } - // Properly prefixed, insert bitwidth. - name.replace(pos, 1, tmp); - } else { - if (pos && (name[pos-1] == 'i' || name[pos-1] == 'f')) { - // Wrong type character. - ti->error("has invalid parameter type for intrinsic %s: %s is not a%s type", - name.c_str(), T->toChars(), - (name[pos-1] == 'i' ? "n integral" : " floating-point")); - } else { - // Just plain wrong. (Error in declaration, not instantiation) - td->error("has an invalid intrinsic name: %s", name.c_str()); - } - fatal(); // or LLVM asserts - } - } - - IF_LOG Logger::println("final intrinsic name: %s", name.c_str()); + IF_LOG Logger::println("final intrinsic name: %s", name.c_str()); } ////////////////////////////////////////////////////////////////////////////////////////// -bool hasUnalignedFields(Type* t) -{ - t = t->toBasetype(); - if (t->ty == Tsarray) { - assert(t->nextOf()->size() % t->nextOf()->alignsize() == 0); - return hasUnalignedFields(t->nextOf()); - } else if (t->ty != Tstruct) - return false; - - TypeStruct* ts = static_cast(t); - if (ts->unaligned) - return (ts->unaligned == 2); - - StructDeclaration* sym = ts->sym; - - // go through all the fields and try to find something unaligned - ts->unaligned = 2; - for (unsigned i = 0; i < sym->fields.dim; i++) - { - VarDeclaration* f = static_cast(sym->fields.data[i]); - unsigned a = f->type->alignsize() - 1; - if (((f->offset + a) & ~a) != f->offset) - return true; - else if (f->type->toBasetype()->ty == Tstruct && hasUnalignedFields(f->type)) - return true; - } - - ts->unaligned = 1; +bool hasUnalignedFields(Type *t) { + t = t->toBasetype(); + if (t->ty == Tsarray) { + assert(t->nextOf()->size() % t->nextOf()->alignsize() == 0); + return hasUnalignedFields(t->nextOf()); + } else if (t->ty != Tstruct) return false; + + TypeStruct *ts = static_cast(t); + if (ts->unaligned) + return (ts->unaligned == 2); + + StructDeclaration *sym = ts->sym; + + // go through all the fields and try to find something unaligned + ts->unaligned = 2; + for (unsigned i = 0; i < sym->fields.dim; i++) { + VarDeclaration *f = static_cast(sym->fields.data[i]); + unsigned a = f->type->alignsize() - 1; + if (((f->offset + a) & ~a) != f->offset) + return true; + else if (f->type->toBasetype()->ty == Tstruct && + hasUnalignedFields(f->type)) + return true; + } + + ts->unaligned = 1; + return false; } ////////////////////////////////////////////////////////////////////////////////////////// -size_t getMemberSize(Type* type) -{ - const dinteger_t dSize = type->size(); - llvm::Type * const llType = DtoType(type); - if (!llType->isSized()) { - // Forward reference in a cycle or similar, we need to trust the D type. - return dSize; +size_t getMemberSize(Type *type) { + const dinteger_t dSize = type->size(); + llvm::Type *const llType = DtoType(type); + if (!llType->isSized()) { + // Forward reference in a cycle or similar, we need to trust the D type. + return dSize; + } + + const uint64_t llSize = gDataLayout->getTypeAllocSize(llType); + assert(llSize <= dSize && + "LLVM type is bigger than the corresponding D type, " + "might lead to aggregate layout mismatch."); + + return llSize; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +Type *stripModifiers(Type *type, bool transitive) { + if (type->ty == Tfunction) + return type; + + if (transitive) + return type->unqualify(MODimmutable | MODconst | MODwild); + else + return type->castMod(0); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLValue *makeLValue(Loc &loc, DValue *value) { + Type *valueType = value->getType(); + bool needsMemory; + LLValue *valuePointer; + if (value->isIm()) { + valuePointer = value->getRVal(); + needsMemory = !DtoIsPassedByRef(valueType); + } else if (value->isVar()) { + valuePointer = value->getLVal(); + needsMemory = false; + } else if (value->isConst()) { + valuePointer = value->getRVal(); + needsMemory = true; + } else { + valuePointer = DtoAlloca(valueType, ".makelvaluetmp"); + DVarValue var(valueType, valuePointer); + DtoAssign(loc, &var, value); + needsMemory = false; + } + + if (needsMemory) + valuePointer = DtoAllocaDump(value, ".makelvaluetmp"); + + return valuePointer; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void callPostblit(Loc &loc, Expression *exp, LLValue *val) { + + Type *tb = exp->type->toBasetype(); + if ((exp->op == TOKvar || exp->op == TOKdotvar || exp->op == TOKstar || + exp->op == TOKthis || exp->op == TOKindex) && + tb->ty == Tstruct) { + StructDeclaration *sd = static_cast(tb)->sym; + if (sd->postblit) { + FuncDeclaration *fd = sd->postblit; + if (fd->storage_class & STCdisable) + fd->toParent()->error( + loc, "is not copyable because it is annotated with @disable"); + DtoResolveFunction(fd); + Expressions args; + DFuncValue dfn(fd, getIrFunc(fd)->func, val); + DtoCallFunction(loc, Type::basic[Tvoid], &dfn, &args); } - - const uint64_t llSize = gDataLayout->getTypeAllocSize(llType); - assert(llSize <= dSize && "LLVM type is bigger than the corresponding D type, " - "might lead to aggregate layout mismatch."); - - return llSize; + } } ////////////////////////////////////////////////////////////////////////////////////////// -Type * stripModifiers(Type * type, bool transitive) -{ - if (type->ty == Tfunction) - return type; - - if (transitive) - return type->unqualify(MODimmutable | MODconst | MODwild); - else - return type->castMod(0); +bool isSpecialRefVar(VarDeclaration *vd) { + return (vd->storage_class & STCref) && (vd->storage_class & STCforeach); } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* makeLValue(Loc& loc, DValue* value) -{ - Type* valueType = value->getType(); - bool needsMemory; - LLValue* valuePointer; - if (value->isIm()) { - valuePointer = value->getRVal(); - needsMemory = !DtoIsPassedByRef(valueType); - } - else if (value->isVar()) { - valuePointer = value->getLVal(); - needsMemory = false; - } - else if (value->isConst()) { - valuePointer = value->getRVal(); - needsMemory = true; - } - else { - valuePointer = DtoAlloca(valueType, ".makelvaluetmp"); - DVarValue var(valueType, valuePointer); - DtoAssign(loc, &var, value); - needsMemory = false; - } +bool isLLVMUnsigned(Type *t) { return t->isunsigned() || t->ty == Tpointer; } - if (needsMemory) - valuePointer = DtoAllocaDump(value, ".makelvaluetmp"); +////////////////////////////////////////////////////////////////////////////////////////// - return valuePointer; +void printLabelName(std::ostream &target, const char *func_mangle, + const char *label_name) { + target << gTargetMachine->getMCAsmInfo()->getPrivateGlobalPrefix() + << func_mangle << "_" << label_name; } ////////////////////////////////////////////////////////////////////////////////////////// -void callPostblit(Loc& loc, Expression *exp, LLValue *val) -{ - - Type *tb = exp->type->toBasetype(); - if ((exp->op == TOKvar || exp->op == TOKdotvar || exp->op == TOKstar || exp->op == TOKthis || exp->op == TOKindex) && - tb->ty == Tstruct) - { StructDeclaration *sd = static_cast(tb)->sym; - if (sd->postblit) - { - FuncDeclaration *fd = sd->postblit; - if (fd->storage_class & STCdisable) - fd->toParent()->error(loc, "is not copyable because it is annotated with @disable"); - DtoResolveFunction(fd); - Expressions args; - DFuncValue dfn(fd, getIrFunc(fd)->func, val); - DtoCallFunction(loc, Type::basic[Tvoid], &dfn, &args); - } - } +void AppendFunctionToLLVMGlobalCtorsDtors(llvm::Function *func, + const uint32_t priority, + const bool isCtor) { + if (isCtor) + llvm::appendToGlobalCtors(gIR->module, func, priority); + else + llvm::appendToGlobalDtors(gIR->module, func, priority); } ////////////////////////////////////////////////////////////////////////////////////////// -bool isSpecialRefVar(VarDeclaration* vd) -{ - return (vd->storage_class & STCref) && (vd->storage_class & STCforeach); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -bool isLLVMUnsigned(Type* t) -{ - return t->isunsigned() || t->ty == Tpointer; -} - -////////////////////////////////////////////////////////////////////////////////////////// - -void printLabelName(std::ostream& target, const char* func_mangle, const char* label_name) -{ - target << gTargetMachine->getMCAsmInfo()->getPrivateGlobalPrefix() << - func_mangle << "_" << label_name; -} - -////////////////////////////////////////////////////////////////////////////////////////// - -void AppendFunctionToLLVMGlobalCtorsDtors(llvm::Function* func, const uint32_t priority, const bool isCtor) -{ - if (isCtor) - llvm::appendToGlobalCtors(gIR->module, func, priority); - else - llvm::appendToGlobalDtors(gIR->module, func, priority); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -void tokToIcmpPred(TOK op, bool isUnsigned, llvm::ICmpInst::Predicate* outPred, llvm::Value** outConst) -{ - switch(op) - { - case TOKlt: - case TOKul: - *outPred = isUnsigned ? llvm::ICmpInst::ICMP_ULT : llvm::ICmpInst::ICMP_SLT; - break; - case TOKle: - case TOKule: - *outPred = isUnsigned ? llvm::ICmpInst::ICMP_ULE : llvm::ICmpInst::ICMP_SLE; - break; - case TOKgt: - case TOKug: - *outPred = isUnsigned ? llvm::ICmpInst::ICMP_UGT : llvm::ICmpInst::ICMP_SGT; - break; - case TOKge: - case TOKuge: - *outPred = isUnsigned ? llvm::ICmpInst::ICMP_UGE : llvm::ICmpInst::ICMP_SGE; - break; - case TOKue: - *outPred = llvm::ICmpInst::ICMP_EQ; - break; - case TOKlg: - *outPred = llvm::ICmpInst::ICMP_NE; - break; - case TOKleg: - *outConst = LLConstantInt::getTrue(gIR->context()); - break; - case TOKunord: - *outConst = LLConstantInt::getFalse(gIR->context()); - break; - default: - llvm_unreachable("Invalid comparison operation"); - } +void tokToIcmpPred(TOK op, bool isUnsigned, llvm::ICmpInst::Predicate *outPred, + llvm::Value **outConst) { + switch (op) { + case TOKlt: + case TOKul: + *outPred = isUnsigned ? llvm::ICmpInst::ICMP_ULT : llvm::ICmpInst::ICMP_SLT; + break; + case TOKle: + case TOKule: + *outPred = isUnsigned ? llvm::ICmpInst::ICMP_ULE : llvm::ICmpInst::ICMP_SLE; + break; + case TOKgt: + case TOKug: + *outPred = isUnsigned ? llvm::ICmpInst::ICMP_UGT : llvm::ICmpInst::ICMP_SGT; + break; + case TOKge: + case TOKuge: + *outPred = isUnsigned ? llvm::ICmpInst::ICMP_UGE : llvm::ICmpInst::ICMP_SGE; + break; + case TOKue: + *outPred = llvm::ICmpInst::ICMP_EQ; + break; + case TOKlg: + *outPred = llvm::ICmpInst::ICMP_NE; + break; + case TOKleg: + *outConst = LLConstantInt::getTrue(gIR->context()); + break; + case TOKunord: + *outConst = LLConstantInt::getFalse(gIR->context()); + break; + default: + llvm_unreachable("Invalid comparison operation"); + } } /////////////////////////////////////////////////////////////////////////////// -DValue* DtoSymbolAddress(Loc& loc, Type* type, Declaration* decl) -{ - IF_LOG Logger::println("DtoSymbolAddress ('%s' of type '%s')", - decl->toChars(), decl->type->toChars()); - LOG_SCOPE +DValue *DtoSymbolAddress(Loc &loc, Type *type, Declaration *decl) { + IF_LOG Logger::println("DtoSymbolAddress ('%s' of type '%s')", + decl->toChars(), decl->type->toChars()); + LOG_SCOPE - if (VarDeclaration* vd = decl->isVarDeclaration()) - { - // The magic variable __ctfe is always false at runtime - if (vd->ident == Id::ctfe) - { - return new DConstValue(type, DtoConstBool(false)); - } - - // this is an error! must be accessed with DotVarExp - if (vd->needThis()) - { - error(loc, "need 'this' to access member %s", vd->toChars()); - fatal(); - } - - // _arguments - if (vd->ident == Id::_arguments && gIR->func()->_arguments) - { - Logger::println("Id::_arguments"); - LLValue* v = gIR->func()->_arguments; - return new DVarValue(type, vd, v); - } - // _argptr - else if (vd->ident == Id::_argptr && gIR->func()->_argptr) - { - Logger::println("Id::_argptr"); - LLValue* v = gIR->func()->_argptr; - return new DVarValue(type, vd, v); - } - // _dollar - else if (vd->ident == Id::dollar) - { - Logger::println("Id::dollar"); - LLValue* val = 0; - if (isIrVarCreated(vd) && (val = getIrValue(vd))) - { - // It must be length of a range - return new DVarValue(type, vd, val); - } - assert(!gIR->arrays.empty()); - val = DtoArrayLen(gIR->arrays.back()); - return new DImValue(type, val); - } - // typeinfo - else if (TypeInfoDeclaration* tid = vd->isTypeInfoDeclaration()) - { - Logger::println("TypeInfoDeclaration"); - DtoResolveTypeInfo(tid); - assert(getIrValue(tid)); - LLType* vartype = DtoType(type); - LLValue* m = getIrValue(tid); - if (m->getType() != getPtrToType(vartype)) - m = gIR->ir->CreateBitCast(m, vartype); - return new DImValue(type, m); - } - // nested variable - else if (vd->nestedrefs.dim) - { - Logger::println("nested variable"); - return DtoNestedVariable(loc, type, vd); - } - // function parameter - else if (vd->isParameter()) - { - IF_LOG { - Logger::println("function param"); - Logger::println("type: %s", vd->type->toChars()); - } - FuncDeclaration* fd = vd->toParent2()->isFuncDeclaration(); - if (fd && fd != gIR->func()->decl) - { - Logger::println("nested parameter"); - return DtoNestedVariable(loc, type, vd); - } - else if (vd->storage_class & STClazy) - { - Logger::println("lazy parameter"); - assert(type->ty == Tdelegate); - return new DVarValue(type, getIrValue(vd)); - } - else if (vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type) || - llvm::isa(getIrValue(vd))) - { - return new DVarValue(type, vd, getIrValue(vd)); - } - else if (llvm::isa(getIrValue(vd))) - { - return new DImValue(type, getIrValue(vd)); - } - else llvm_unreachable("Unexpected parameter value."); - } - else - { - Logger::println("a normal variable"); - - // take care of forward references of global variables - const bool isGlobal = vd->isDataseg() || (vd->storage_class & STCextern); - if (isGlobal) - DtoResolveVariable(vd); - - assert(isIrVarCreated(vd) && "Variable not resolved."); - - llvm::Value* val = getIrValue(vd); - assert(val && "Variable value not set yet."); - - if (isGlobal) - { - llvm::Type* expectedType = llvm::PointerType::getUnqual(DtoMemType(type)); - // The type of globals is determined by their initializer, so - // we might need to cast. Make sure that the type sizes fit - - // '==' instead of '<=' should probably work as well. - if (val->getType() != expectedType) - { - llvm::Type* t = llvm::cast(val->getType())->getElementType(); - assert(getTypeStoreSize(DtoType(type)) <= getTypeStoreSize(t) && - "Global type mismatch, encountered type too small."); - val = DtoBitCast(val, expectedType); - } - } - - return new DVarValue(type, vd, val); - } + if (VarDeclaration *vd = decl->isVarDeclaration()) { + // The magic variable __ctfe is always false at runtime + if (vd->ident == Id::ctfe) { + return new DConstValue(type, DtoConstBool(false)); } - if (FuncDeclaration* fdecl = decl->isFuncDeclaration()) - { - Logger::println("FuncDeclaration"); - fdecl = fdecl->toAliasFunc(); - if (fdecl->llvmInternal == LLVMinline_asm) - { - // TODO: Is this needed? If so, what about other intrinsics? - error(loc, "special ldc inline asm is not a normal function"); - fatal(); - } - DtoResolveFunction(fdecl); - return new DFuncValue(fdecl, fdecl->llvmInternal != LLVMva_arg ? getIrFunc(fdecl)->func : 0); + // this is an error! must be accessed with DotVarExp + if (vd->needThis()) { + error(loc, "need 'this' to access member %s", vd->toChars()); + fatal(); } - if (SymbolDeclaration* sdecl = decl->isSymbolDeclaration()) - { - // this seems to be the static initialiser for structs - Type* sdecltype = sdecl->type->toBasetype(); - IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); - assert(sdecltype->ty == Tstruct); - TypeStruct* ts = static_cast(sdecltype); - assert(ts->sym); - DtoResolveStruct(ts->sym); - - LLValue* initsym = getIrAggr(ts->sym)->getInitSymbol(); - initsym = DtoBitCast(initsym, DtoType(ts->pointerTo())); - return new DVarValue(type, initsym); + // _arguments + if (vd->ident == Id::_arguments && gIR->func()->_arguments) { + Logger::println("Id::_arguments"); + LLValue *v = gIR->func()->_arguments; + return new DVarValue(type, vd, v); } - - llvm_unreachable("Unimplemented VarExp type"); -} - -llvm::Constant* DtoConstSymbolAddress(Loc& loc, Declaration* decl) -{ - // Make sure 'this' isn't needed. - // TODO: This check really does not belong here, should be moved to - // semantic analysis in the frontend. - if (decl->needThis()) - { - error(loc, "need 'this' to access %s", decl->toChars()); - fatal(); + // _argptr + else if (vd->ident == Id::_argptr && gIR->func()->_argptr) { + Logger::println("Id::_argptr"); + LLValue *v = gIR->func()->_argptr; + return new DVarValue(type, vd, v); } + // _dollar + else if (vd->ident == Id::dollar) { + Logger::println("Id::dollar"); + LLValue *val = 0; + if (isIrVarCreated(vd) && (val = getIrValue(vd))) { + // It must be length of a range + return new DVarValue(type, vd, val); + } + assert(!gIR->arrays.empty()); + val = DtoArrayLen(gIR->arrays.back()); + return new DImValue(type, val); + } + // typeinfo + else if (TypeInfoDeclaration *tid = vd->isTypeInfoDeclaration()) { + Logger::println("TypeInfoDeclaration"); + DtoResolveTypeInfo(tid); + assert(getIrValue(tid)); + LLType *vartype = DtoType(type); + LLValue *m = getIrValue(tid); + if (m->getType() != getPtrToType(vartype)) + m = gIR->ir->CreateBitCast(m, vartype); + return new DImValue(type, m); + } + // nested variable + else if (vd->nestedrefs.dim) { + Logger::println("nested variable"); + return DtoNestedVariable(loc, type, vd); + } + // function parameter + else if (vd->isParameter()) { + IF_LOG { + Logger::println("function param"); + Logger::println("type: %s", vd->type->toChars()); + } + FuncDeclaration *fd = vd->toParent2()->isFuncDeclaration(); + if (fd && fd != gIR->func()->decl) { + Logger::println("nested parameter"); + return DtoNestedVariable(loc, type, vd); + } else if (vd->storage_class & STClazy) { + Logger::println("lazy parameter"); + assert(type->ty == Tdelegate); + return new DVarValue(type, getIrValue(vd)); + } else if (vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type) || + llvm::isa(getIrValue(vd))) { + return new DVarValue(type, vd, getIrValue(vd)); + } else if (llvm::isa(getIrValue(vd))) { + return new DImValue(type, getIrValue(vd)); + } else + llvm_unreachable("Unexpected parameter value."); + } else { + Logger::println("a normal variable"); - // global variable - if (VarDeclaration* vd = decl->isVarDeclaration()) - { - if (!vd->isDataseg()) - { - // Not sure if this can be triggered from user code, but it is - // needed for the current hacky implementation of - // AssocArrayLiteralExp::toElem, which requires on error - // gagging to check for constantness of the initializer. - error(loc, "cannot use address of non-global variable '%s' " - "as constant initializer", vd->toChars()); - if (!global.gag) fatal(); - return NULL; - } - + // take care of forward references of global variables + const bool isGlobal = vd->isDataseg() || (vd->storage_class & STCextern); + if (isGlobal) DtoResolveVariable(vd); - LLConstant* llc = llvm::dyn_cast(getIrValue(vd)); - assert(llc); - return llc; - } - // static function - else if (FuncDeclaration* fd = decl->isFuncDeclaration()) - { - DtoResolveFunction(fd); - return getIrFunc(fd)->func; - } - llvm_unreachable("Taking constant address not implemented."); + assert(isIrVarCreated(vd) && "Variable not resolved."); + + llvm::Value *val = getIrValue(vd); + assert(val && "Variable value not set yet."); + + if (isGlobal) { + llvm::Type *expectedType = + llvm::PointerType::getUnqual(DtoMemType(type)); + // The type of globals is determined by their initializer, so + // we might need to cast. Make sure that the type sizes fit - + // '==' instead of '<=' should probably work as well. + if (val->getType() != expectedType) { + llvm::Type *t = + llvm::cast(val->getType())->getElementType(); + assert(getTypeStoreSize(DtoType(type)) <= getTypeStoreSize(t) && + "Global type mismatch, encountered type too small."); + val = DtoBitCast(val, expectedType); + } + } + + return new DVarValue(type, vd, val); + } + } + + if (FuncDeclaration *fdecl = decl->isFuncDeclaration()) { + Logger::println("FuncDeclaration"); + fdecl = fdecl->toAliasFunc(); + if (fdecl->llvmInternal == LLVMinline_asm) { + // TODO: Is this needed? If so, what about other intrinsics? + error(loc, "special ldc inline asm is not a normal function"); + fatal(); + } + DtoResolveFunction(fdecl); + return new DFuncValue( + fdecl, fdecl->llvmInternal != LLVMva_arg ? getIrFunc(fdecl)->func : 0); + } + + if (SymbolDeclaration *sdecl = decl->isSymbolDeclaration()) { + // this seems to be the static initialiser for structs + Type *sdecltype = sdecl->type->toBasetype(); + IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); + assert(sdecltype->ty == Tstruct); + TypeStruct *ts = static_cast(sdecltype); + assert(ts->sym); + DtoResolveStruct(ts->sym); + + LLValue *initsym = getIrAggr(ts->sym)->getInitSymbol(); + initsym = DtoBitCast(initsym, DtoType(ts->pointerTo())); + return new DVarValue(type, initsym); + } + + llvm_unreachable("Unimplemented VarExp type"); } -llvm::GlobalVariable* getOrCreateGlobal(Loc& loc, llvm::Module& module, - llvm::Type* type, bool isConstant, llvm::GlobalValue::LinkageTypes linkage, - llvm::Constant* init, llvm::StringRef name, bool isThreadLocal) -{ - llvm::GlobalVariable* existing = module.getGlobalVariable(name, true); - if (existing) - { - if (existing->getType()->getElementType() != type) - { - error(loc, "Global variable type does not match previous " - "declaration with same mangled name: %s", name.str().c_str()); - fatal(); - } - return existing; +llvm::Constant *DtoConstSymbolAddress(Loc &loc, Declaration *decl) { + // Make sure 'this' isn't needed. + // TODO: This check really does not belong here, should be moved to + // semantic analysis in the frontend. + if (decl->needThis()) { + error(loc, "need 'this' to access %s", decl->toChars()); + fatal(); + } + + // global variable + if (VarDeclaration *vd = decl->isVarDeclaration()) { + if (!vd->isDataseg()) { + // Not sure if this can be triggered from user code, but it is + // needed for the current hacky implementation of + // AssocArrayLiteralExp::toElem, which requires on error + // gagging to check for constantness of the initializer. + error(loc, "cannot use address of non-global variable '%s' " + "as constant initializer", + vd->toChars()); + if (!global.gag) + fatal(); + return NULL; } - // Use a command line option for the thread model. - // On PPC there is only local-exec available - in this case just ignore the - // command line. - const llvm::GlobalVariable::ThreadLocalMode tlsModel = - isThreadLocal - ? (global.params.targetTriple.getArch() == llvm::Triple::ppc - ? llvm::GlobalVariable::LocalExecTLSModel - : clThreadModel.getValue()) - : llvm::GlobalVariable::NotThreadLocal; - return new llvm::GlobalVariable(module, type, isConstant, linkage, - init, name, 0, tlsModel); + DtoResolveVariable(vd); + LLConstant *llc = llvm::dyn_cast(getIrValue(vd)); + assert(llc); + return llc; + } + // static function + else if (FuncDeclaration *fd = decl->isFuncDeclaration()) { + DtoResolveFunction(fd); + return getIrFunc(fd)->func; + } + + llvm_unreachable("Taking constant address not implemented."); } -FuncDeclaration* getParentFunc(Dsymbol* sym, bool stopOnStatic) -{ - if (!sym) - return NULL; - - // check if symbol is itself a static function/aggregate - if (stopOnStatic) - { - // Static functions and function (not delegate) literals don't allow - // access to a parent context, even if they are nested. - if (FuncDeclaration* fd = sym->isFuncDeclaration()) - { - bool certainlyNewRoot = fd->isStatic() || - (fd->isFuncLiteralDeclaration() && - static_cast(fd)->tok == TOKfunction); - if (certainlyNewRoot) - return NULL; - } - // Fun fact: AggregateDeclarations are not Declarations. - else if (AggregateDeclaration* ad = sym->isAggregateDeclaration()) - { - if (!ad->isNested()) - return NULL; - } +llvm::GlobalVariable *getOrCreateGlobal(Loc &loc, llvm::Module &module, + llvm::Type *type, bool isConstant, + llvm::GlobalValue::LinkageTypes linkage, + llvm::Constant *init, + llvm::StringRef name, + bool isThreadLocal) { + llvm::GlobalVariable *existing = module.getGlobalVariable(name, true); + if (existing) { + if (existing->getType()->getElementType() != type) { + error(loc, "Global variable type does not match previous " + "declaration with same mangled name: %s", + name.str().c_str()); + fatal(); } + return existing; + } - for (Dsymbol* parent = sym->parent; parent; parent = parent->parent) - { - if (FuncDeclaration* fd = parent->isFuncDeclaration()) - return fd; - - if (stopOnStatic) - { - if (AggregateDeclaration* ad = parent->isAggregateDeclaration()) - { - if (!ad->isNested()) - return NULL; - } - } - } + // Use a command line option for the thread model. + // On PPC there is only local-exec available - in this case just ignore the + // command line. + const llvm::GlobalVariable::ThreadLocalMode tlsModel = + isThreadLocal ? (global.params.targetTriple.getArch() == llvm::Triple::ppc + ? llvm::GlobalVariable::LocalExecTLSModel + : clThreadModel.getValue()) + : llvm::GlobalVariable::NotThreadLocal; + return new llvm::GlobalVariable(module, type, isConstant, linkage, init, name, + 0, tlsModel); +} +FuncDeclaration *getParentFunc(Dsymbol *sym, bool stopOnStatic) { + if (!sym) return NULL; -} -LLValue* DtoIndexAggregate(LLValue* src, AggregateDeclaration* ad, VarDeclaration* vd) -{ - IF_LOG Logger::println("Indexing aggregate field %s:", vd->toPrettyChars()); - LOG_SCOPE; - - // Make sure the aggregate is resolved, as subsequent code might expect - // isIrVarCreated(vd). This is a bit of a hack, we don't actually need this - // ourselves, DtoType below would be enough. - DtoResolveDsymbol(ad); - - // Cast the pointer we got to the canonical struct type the indices are - // based on. - LLType* st = DtoType(ad->type); - if (ad->isStructDeclaration()) - st = getPtrToType(st); - src = DtoBitCast(src, st); - - // Look up field to index and any offset to apply. - unsigned fieldIndex; - unsigned byteOffset; - assert(ad->type->ctype->isAggr()); - static_cast(ad->type->ctype)->getMemberLocation( - vd, fieldIndex, byteOffset); - - LLValue* val = DtoGEPi(src, 0, fieldIndex); - - if (byteOffset) - { - // Cast to void* to apply byte-wise offset. - val = DtoBitCast(val, getVoidPtrType()); - val = DtoGEPi1(val, byteOffset); + // check if symbol is itself a static function/aggregate + if (stopOnStatic) { + // Static functions and function (not delegate) literals don't allow + // access to a parent context, even if they are nested. + if (FuncDeclaration *fd = sym->isFuncDeclaration()) { + bool certainlyNewRoot = + fd->isStatic() || + (fd->isFuncLiteralDeclaration() && + static_cast(fd)->tok == TOKfunction); + if (certainlyNewRoot) + return NULL; } + // Fun fact: AggregateDeclarations are not Declarations. + else if (AggregateDeclaration *ad = sym->isAggregateDeclaration()) { + if (!ad->isNested()) + return NULL; + } + } - // Cast the (possibly void*) pointer to the canonical variable type. - val = DtoBitCast(val, DtoPtrToType(vd->type)); + for (Dsymbol *parent = sym->parent; parent; parent = parent->parent) { + if (FuncDeclaration *fd = parent->isFuncDeclaration()) + return fd; - IF_LOG Logger::cout() << "Value: " << *val << '\n'; - return val; + if (stopOnStatic) { + if (AggregateDeclaration *ad = parent->isAggregateDeclaration()) { + if (!ad->isNested()) + return NULL; + } + } + } + + return NULL; } -unsigned getFieldGEPIndex(AggregateDeclaration* ad, VarDeclaration* vd) -{ - unsigned fieldIndex; - unsigned byteOffset; - assert(ad->type->ctype->isAggr()); - static_cast(ad->type->ctype)->getMemberLocation( - vd, fieldIndex, byteOffset); - assert(byteOffset == 0 && "Cannot address field by a simple GEP."); - return fieldIndex; +LLValue *DtoIndexAggregate(LLValue *src, AggregateDeclaration *ad, + VarDeclaration *vd) { + IF_LOG Logger::println("Indexing aggregate field %s:", vd->toPrettyChars()); + LOG_SCOPE; + + // Make sure the aggregate is resolved, as subsequent code might expect + // isIrVarCreated(vd). This is a bit of a hack, we don't actually need this + // ourselves, DtoType below would be enough. + DtoResolveDsymbol(ad); + + // Cast the pointer we got to the canonical struct type the indices are + // based on. + LLType *st = DtoType(ad->type); + if (ad->isStructDeclaration()) + st = getPtrToType(st); + src = DtoBitCast(src, st); + + // Look up field to index and any offset to apply. + unsigned fieldIndex; + unsigned byteOffset; + assert(ad->type->ctype->isAggr()); + static_cast(ad->type->ctype) + ->getMemberLocation(vd, fieldIndex, byteOffset); + + LLValue *val = DtoGEPi(src, 0, fieldIndex); + + if (byteOffset) { + // Cast to void* to apply byte-wise offset. + val = DtoBitCast(val, getVoidPtrType()); + val = DtoGEPi1(val, byteOffset); + } + + // Cast the (possibly void*) pointer to the canonical variable type. + val = DtoBitCast(val, DtoPtrToType(vd->type)); + + IF_LOG Logger::cout() << "Value: " << *val << '\n'; + return val; +} + +unsigned getFieldGEPIndex(AggregateDeclaration *ad, VarDeclaration *vd) { + unsigned fieldIndex; + unsigned byteOffset; + assert(ad->type->ctype->isAggr()); + static_cast(ad->type->ctype) + ->getMemberLocation(vd, fieldIndex, byteOffset); + assert(byteOffset == 0 && "Cannot address field by a simple GEP."); + return fieldIndex; } #if LDC_LLVM_VER >= 307 -bool supportsCOMDAT() -{ - return !global.params.targetTriple.isOSBinFormatMachO(); +bool supportsCOMDAT() { + return !global.params.targetTriple.isOSBinFormatMachO(); } #endif \ No newline at end of file diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index 0dc00f1870..2eb6c3f46c 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -23,71 +23,77 @@ #include "ir/irfuncty.h" // dynamic memory helpers -LLValue* DtoNew(Loc& loc, Type* newtype); -LLValue* DtoNewStruct(Loc& loc, TypeStruct* newtype); -void DtoDeleteMemory(Loc& loc, DValue* ptr); -void DtoDeleteStruct(Loc& loc, DValue* ptr); -void DtoDeleteClass(Loc& loc, DValue* inst); -void DtoDeleteInterface(Loc& loc, DValue* inst); -void DtoDeleteArray(Loc& loc, DValue* arr); +LLValue *DtoNew(Loc &loc, Type *newtype); +LLValue *DtoNewStruct(Loc &loc, TypeStruct *newtype); +void DtoDeleteMemory(Loc &loc, DValue *ptr); +void DtoDeleteStruct(Loc &loc, DValue *ptr); +void DtoDeleteClass(Loc &loc, DValue *inst); +void DtoDeleteInterface(Loc &loc, DValue *inst); +void DtoDeleteArray(Loc &loc, DValue *arr); -unsigned DtoAlignment(Type* type); -unsigned DtoAlignment(VarDeclaration* vd); +unsigned DtoAlignment(Type *type); +unsigned DtoAlignment(VarDeclaration *vd); // emit an alloca -llvm::AllocaInst* DtoAlloca(Type* type, const char* name = ""); -llvm::AllocaInst* DtoAlloca(VarDeclaration* vd, const char* name = ""); -llvm::AllocaInst* DtoArrayAlloca(Type* type, unsigned arraysize, const char* name = ""); -llvm::AllocaInst* DtoRawAlloca(LLType* lltype, size_t alignment, const char* name = ""); -LLValue* DtoGcMalloc(Loc& loc, LLType* lltype, const char* name = ""); +llvm::AllocaInst *DtoAlloca(Type *type, const char *name = ""); +llvm::AllocaInst *DtoAlloca(VarDeclaration *vd, const char *name = ""); +llvm::AllocaInst *DtoArrayAlloca(Type *type, unsigned arraysize, + const char *name = ""); +llvm::AllocaInst *DtoRawAlloca(LLType *lltype, size_t alignment, + const char *name = ""); +LLValue *DtoGcMalloc(Loc &loc, LLType *lltype, const char *name = ""); -LLValue* DtoAllocaDump(DValue* val, const char* name = ""); -LLValue* DtoAllocaDump(DValue* val, Type* asType, const char* name = ""); -LLValue* DtoAllocaDump(DValue* val, LLType* asType, int alignment = 0, const char* name = ""); -LLValue* DtoAllocaDump(LLValue* val, int alignment = 0, const char* name = ""); -LLValue* DtoAllocaDump(LLValue* val, Type* asType, const char* name = ""); -LLValue* DtoAllocaDump(LLValue* val, LLType* asType, int alignment = 0, const char* name = ""); +LLValue *DtoAllocaDump(DValue *val, const char *name = ""); +LLValue *DtoAllocaDump(DValue *val, Type *asType, const char *name = ""); +LLValue *DtoAllocaDump(DValue *val, LLType *asType, int alignment = 0, + const char *name = ""); +LLValue *DtoAllocaDump(LLValue *val, int alignment = 0, const char *name = ""); +LLValue *DtoAllocaDump(LLValue *val, Type *asType, const char *name = ""); +LLValue *DtoAllocaDump(LLValue *val, LLType *asType, int alignment = 0, + const char *name = ""); // assertion generator -void DtoAssert(Module* M, Loc& loc, DValue* msg); +void DtoAssert(Module *M, Loc &loc, DValue *msg); // returns module file name -LLValue* DtoModuleFileName(Module* M, const Loc& loc); +LLValue *DtoModuleFileName(Module *M, const Loc &loc); /// emits goto to LabelStatement with the target identifier void DtoGoto(Loc &loc, LabelDsymbol *target); /// Enters a critical section. -void DtoEnterCritical(Loc& loc, LLValue* g); +void DtoEnterCritical(Loc &loc, LLValue *g); /// leaves a critical section. -void DtoLeaveCritical(Loc& loc, LLValue* g); +void DtoLeaveCritical(Loc &loc, LLValue *g); /// Enters a monitor lock. -void DtoEnterMonitor(Loc& loc, LLValue* v); +void DtoEnterMonitor(Loc &loc, LLValue *v); /// Leaves a monitor lock. -void DtoLeaveMonitor(Loc& loc, LLValue* v); +void DtoLeaveMonitor(Loc &loc, LLValue *v); // basic operations -void DtoAssign(Loc& loc, DValue* lhs, DValue* rhs, int op = -1, bool canSkipPostblit = false); +void DtoAssign(Loc &loc, DValue *lhs, DValue *rhs, int op = -1, + bool canSkipPostblit = false); -DValue* DtoSymbolAddress(Loc& loc, Type* type, Declaration* decl); -llvm::Constant* DtoConstSymbolAddress(Loc& loc,Declaration* decl); +DValue *DtoSymbolAddress(Loc &loc, Type *type, Declaration *decl); +llvm::Constant *DtoConstSymbolAddress(Loc &loc, Declaration *decl); /// Create a null DValue. -DValue* DtoNullValue(Type* t, Loc loc = Loc()); +DValue *DtoNullValue(Type *t, Loc loc = Loc()); // casts -DValue* DtoCastInt(Loc& loc, DValue* val, Type* to); -DValue* DtoCastPtr(Loc& loc, DValue* val, Type* to); -DValue* DtoCastFloat(Loc& loc, DValue* val, Type* to); -DValue* DtoCastDelegate(Loc& loc, DValue* val, Type* to); -DValue* DtoCast(Loc& loc, DValue* val, Type* to); +DValue *DtoCastInt(Loc &loc, DValue *val, Type *to); +DValue *DtoCastPtr(Loc &loc, DValue *val, Type *to); +DValue *DtoCastFloat(Loc &loc, DValue *val, Type *to); +DValue *DtoCastDelegate(Loc &loc, DValue *val, Type *to); +DValue *DtoCast(Loc &loc, DValue *val, Type *to); -// return the same val as passed in, modified to the target type, if possible, otherwise returns a new DValue -DValue* DtoPaintType(Loc& loc, DValue* val, Type* to); +// return the same val as passed in, modified to the target type, if possible, +// otherwise returns a new DValue +DValue *DtoPaintType(Loc &loc, DValue *val, Type *to); // is template instance check, returns module where instantiated -TemplateInstance* DtoIsTemplateInstance(Dsymbol* s); +TemplateInstance *DtoIsTemplateInstance(Dsymbol *s); /// Makes sure the declarations corresponding to the given D symbol have been /// emitted to the currently processed LLVM module. @@ -96,66 +102,68 @@ TemplateInstance* DtoIsTemplateInstance(Dsymbol* s); /// /// This function does *not* emit any (function, variable) *definitions*; this /// is done by Dsymbol::codegen. -void DtoResolveDsymbol(Dsymbol* dsym); -void DtoResolveVariable(VarDeclaration* var); +void DtoResolveDsymbol(Dsymbol *dsym); +void DtoResolveVariable(VarDeclaration *var); // declaration inside a declarationexp -void DtoVarDeclaration(VarDeclaration* var); -DValue* DtoDeclarationExp(Dsymbol* declaration); -LLValue* DtoRawVarDeclaration(VarDeclaration* var, LLValue* addr = 0); +void DtoVarDeclaration(VarDeclaration *var); +DValue *DtoDeclarationExp(Dsymbol *declaration); +LLValue *DtoRawVarDeclaration(VarDeclaration *var, LLValue *addr = 0); // initializer helpers -LLConstant* DtoConstInitializer(Loc& loc, Type* type, Initializer* init); -LLConstant* DtoConstExpInit(Loc& loc, Type* targetType, Expression* exp); +LLConstant *DtoConstInitializer(Loc &loc, Type *type, Initializer *init); +LLConstant *DtoConstExpInit(Loc &loc, Type *targetType, Expression *exp); // getting typeinfo of type, base=true casts to object.TypeInfo -LLConstant* DtoTypeInfoOf(Type* ty, bool base=true); +LLConstant *DtoTypeInfoOf(Type *ty, bool base = true); // binary operations -DValue* DtoBinAdd(DValue* lhs, DValue* rhs); -DValue* DtoBinSub(DValue* lhs, DValue* rhs); +DValue *DtoBinAdd(DValue *lhs, DValue *rhs); +DValue *DtoBinSub(DValue *lhs, DValue *rhs); // these binops need an explicit result type to handling // to give 'ifloat op float' and 'float op ifloat' the correct type -DValue* DtoBinMul(Type* resulttype, DValue* lhs, DValue* rhs); -DValue* DtoBinDiv(Type* resulttype, DValue* lhs, DValue* rhs); -DValue* DtoBinRem(Type* resulttype, DValue* lhs, DValue* rhs); -LLValue* DtoBinNumericEquals(Loc& loc, DValue* lhs, DValue* rhs, TOK op); -LLValue* DtoBinFloatsEquals(Loc& loc, DValue* lhs, DValue* rhs, TOK op); +DValue *DtoBinMul(Type *resulttype, DValue *lhs, DValue *rhs); +DValue *DtoBinDiv(Type *resulttype, DValue *lhs, DValue *rhs); +DValue *DtoBinRem(Type *resulttype, DValue *lhs, DValue *rhs); +LLValue *DtoBinNumericEquals(Loc &loc, DValue *lhs, DValue *rhs, TOK op); +LLValue *DtoBinFloatsEquals(Loc &loc, DValue *lhs, DValue *rhs, TOK op); // target stuff void findDefaultTarget(); /// Fixup an overloaded intrinsic name string. -void DtoOverloadedIntrinsicName(TemplateInstance* ti, TemplateDeclaration* td, std::string& name); +void DtoOverloadedIntrinsicName(TemplateInstance *ti, TemplateDeclaration *td, + std::string &name); /// Returns true if there is any unaligned type inside the aggregate. -bool hasUnalignedFields(Type* t); +bool hasUnalignedFields(Type *t); /// Returns a pointer to the given member field of an aggregate. /// /// 'src' is a pointer to the start of the memory of an 'ad' instance. -LLValue* DtoIndexAggregate(LLValue* src, AggregateDeclaration* ad, VarDeclaration* vd); +LLValue *DtoIndexAggregate(LLValue *src, AggregateDeclaration *ad, + VarDeclaration *vd); /// Returns the index of a given member variable in the resulting LLVM type of /// an aggregate. /// /// This is only a valid operation if the field is known to be non-overlapping, /// so that no byte-wise offset is needed. -unsigned getFieldGEPIndex(AggregateDeclaration* ad, VarDeclaration* vd); +unsigned getFieldGEPIndex(AggregateDeclaration *ad, VarDeclaration *vd); /// -DValue* DtoInlineAsmExpr(Loc& loc, FuncDeclaration* fd, Expressions* arguments); +DValue *DtoInlineAsmExpr(Loc &loc, FuncDeclaration *fd, Expressions *arguments); /// Returns the size the LLVM type for a member variable of the given type will /// take up in a struct (in bytes). This does not include padding in any way. -size_t getMemberSize(Type* type); +size_t getMemberSize(Type *type); /// Returns the llvm::Value of the passed DValue, making sure that it is an /// lvalue (has a memory address), so it can be passed to the D runtime /// functions without problems. -LLValue* makeLValue(Loc& loc, DValue* value); +LLValue *makeLValue(Loc &loc, DValue *value); -void callPostblit(Loc& loc, Expression *exp, LLValue *val); +void callPostblit(Loc &loc, Expression *exp, LLValue *val); /// Returns whether the given variable is a DMD-internal "ref variable". /// @@ -163,75 +171,82 @@ void callPostblit(Loc& loc, Expression *exp, LLValue *val); /// function signatures and foreach headers), but the DMD frontend internally /// creates them in cases like lowering a ref foreach to a for loop or the /// implicit __result variable for ref-return functions with out contracts. -bool isSpecialRefVar(VarDeclaration* vd); +bool isSpecialRefVar(VarDeclaration *vd); /// Returns whether the type is unsigned in LLVM terms, which also includes /// pointers. -bool isLLVMUnsigned(Type* t); +bool isLLVMUnsigned(Type *t); /// Converts a DMD comparison operation token into the corresponding LLVM icmp /// predicate for the given operand signedness. /// /// For some operations, the result can be a constant. In this case outConst is /// set to it, otherwise outPred is set to the predicate to use. -void tokToIcmpPred(TOK op, bool isUnsigned, llvm::ICmpInst::Predicate* outPred, - llvm::Value** outConst); +void tokToIcmpPred(TOK op, bool isUnsigned, llvm::ICmpInst::Predicate *outPred, + llvm::Value **outConst); //////////////////////////////////////////// // gen/tocall.cpp stuff below //////////////////////////////////////////// /// -IrFuncTy &DtoIrTypeFunction(DValue* fnval); +IrFuncTy &DtoIrTypeFunction(DValue *fnval); /// -TypeFunction* DtoTypeFunction(DValue* fnval); +TypeFunction *DtoTypeFunction(DValue *fnval); /// -LLValue* DtoCallableValue(DValue* fn); +LLValue *DtoCallableValue(DValue *fn); /// -LLFunctionType* DtoExtractFunctionType(LLType* type); +LLFunctionType *DtoExtractFunctionType(LLType *type); /// Checks whether fndecl is an intrinsic that requires special lowering. If so, /// emits the code for it and returns true, settings result to the resulting /// DValue (if any). If the call does not correspond to a "magic" intrinsic, /// i.e. should be turned into a normal function call, returns false. -bool DtoLowerMagicIntrinsic(IRState* p, FuncDeclaration* fndecl, CallExp *e, DValue*& result); +bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, + DValue *&result); /// -DValue* DtoCallFunction(Loc& loc, Type* resulttype, DValue* fnval, Expressions* arguments, LLValue* retvar = 0); +DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, + Expressions *arguments, LLValue *retvar = 0); -Type* stripModifiers(Type* type, bool transitive = false); +Type *stripModifiers(Type *type, bool transitive = false); -void printLabelName(std::ostream& target, const char* func_mangle, const char* label_name); +void printLabelName(std::ostream &target, const char *func_mangle, + const char *label_name); -void AppendFunctionToLLVMGlobalCtorsDtors(llvm::Function* func, const uint32_t priority, const bool isCtor); +void AppendFunctionToLLVMGlobalCtorsDtors(llvm::Function *func, + const uint32_t priority, + const bool isCtor); template -LLConstant* toConstantArray(LLType* ct, LLArrayType* at, T* str, size_t len, bool nullterm = true) -{ - std::vector vals; - vals.reserve(len+1); - for (size_t i = 0; i < len; ++i) { - vals.push_back(LLConstantInt::get(ct, str[i], false)); - } - if (nullterm) - vals.push_back(LLConstantInt::get(ct, 0, false)); - return LLConstantArray::get(at, vals); +LLConstant *toConstantArray(LLType *ct, LLArrayType *at, T *str, size_t len, + bool nullterm = true) { + std::vector vals; + vals.reserve(len + 1); + for (size_t i = 0; i < len; ++i) { + vals.push_back(LLConstantInt::get(ct, str[i], false)); + } + if (nullterm) + vals.push_back(LLConstantInt::get(ct, 0, false)); + return LLConstantArray::get(at, vals); } - /// Tries to create an LLVM global with the given properties. If a variable with /// the same mangled name already exists, checks if the types match and returns /// it instead. /// /// Necessary to support multiple declarations with the same mangled name, as /// can be the case due to pragma(mangle). -llvm::GlobalVariable* getOrCreateGlobal(Loc& loc, llvm::Module& module, - llvm::Type* type, bool isConstant, llvm::GlobalValue::LinkageTypes linkage, - llvm::Constant* init, llvm::StringRef name, bool isThreadLocal = false); +llvm::GlobalVariable *getOrCreateGlobal(Loc &loc, llvm::Module &module, + llvm::Type *type, bool isConstant, + llvm::GlobalValue::LinkageTypes linkage, + llvm::Constant *init, + llvm::StringRef name, + bool isThreadLocal = false); -FuncDeclaration* getParentFunc(Dsymbol* sym, bool stopOnStatic); +FuncDeclaration *getParentFunc(Dsymbol *sym, bool stopOnStatic); void Declaration_codegen(Dsymbol *decl); void Declaration_codegen(Dsymbol *decl, IRState *irs); @@ -244,12 +259,14 @@ LLConstant *toConstElem(Expression *e, IRState *p); #if LDC_LLVM_VER >= 307 bool supportsCOMDAT(); -#define SET_COMDAT(x,m) if (supportsCOMDAT()) x->setComdat(m.getOrInsertComdat(x->getName())) +#define SET_COMDAT(x, m) \ + if (supportsCOMDAT()) \ + x->setComdat(m.getOrInsertComdat(x->getName())) #else #define supportsCOMDAT() false -#define SET_COMDAT(x,m) +#define SET_COMDAT(x, m) #endif diff --git a/gen/logger.cpp b/gen/logger.cpp index 579ae9e9c6..bb633aed56 100644 --- a/gen/logger.cpp +++ b/gen/logger.cpp @@ -25,102 +25,92 @@ #include "gen/logger.h" #include "gen/irstate.h" -void Stream::writeType(std::ostream& OS, const llvm::Type& Ty) { - llvm::raw_os_ostream raw(OS); - Ty.print(raw); +void Stream::writeType(std::ostream &OS, const llvm::Type &Ty) { + llvm::raw_os_ostream raw(OS); + Ty.print(raw); } -void Stream::writeValue(std::ostream& OS, const llvm::Value& V) { - // Constants don't always get their types pretty-printed. - // (Only treat non-global constants like this, so that e.g. global variables - // still get their initializers printed) - llvm::raw_os_ostream raw(OS); - if (llvm::isa(V) && !llvm::isa(V)) - V.printAsOperand(raw, true, &gIR->module); - else - V.print(raw); +void Stream::writeValue(std::ostream &OS, const llvm::Value &V) { + // Constants don't always get their types pretty-printed. + // (Only treat non-global constants like this, so that e.g. global variables + // still get their initializers printed) + llvm::raw_os_ostream raw(OS); + if (llvm::isa(V) && !llvm::isa(V)) + V.printAsOperand(raw, true, &gIR->module); + else + V.print(raw); } -namespace Logger -{ - static std::string indent_str; - bool _enabled; +namespace Logger { +static std::string indent_str; +bool _enabled; - static llvm::cl::opt enabledopt("vv", - llvm::cl::desc("Print front-end/glue code debug log"), - llvm::cl::location(_enabled), - llvm::cl::ZeroOrMore); +static llvm::cl::opt + enabledopt("vv", llvm::cl::desc("Print front-end/glue code debug log"), + llvm::cl::location(_enabled), llvm::cl::ZeroOrMore); - void indent() - { - if (_enabled) { - indent_str += "* "; - } - } - void undent() - { - if (_enabled) { - assert(!indent_str.empty()); - indent_str.resize(indent_str.size()-2); - } - } - Stream cout() - { - if (_enabled) - return std::cout << indent_str; - else - return 0; - } +void indent() { + if (_enabled) { + indent_str += "* "; + } +} +void undent() { + if (_enabled) { + assert(!indent_str.empty()); + indent_str.resize(indent_str.size() - 2); + } +} +Stream cout() { + if (_enabled) + return std::cout << indent_str; + else + return 0; +} #if defined(_MSC_VER) - static inline void - search_and_replace(std::string& str, const std::string& what, const std::string& replacement) - { - assert(!what.empty()); - size_t pos = str.find(what); - while (pos != std::string::npos) - { - str.replace(pos, what.size(), replacement); - pos = str.find(what, pos + replacement.size()); - } - } +static inline void search_and_replace(std::string &str, const std::string &what, + const std::string &replacement) { + assert(!what.empty()); + size_t pos = str.find(what); + while (pos != std::string::npos) { + str.replace(pos, what.size(), replacement); + pos = str.find(what, pos + replacement.size()); + } +} -#define WORKAROUND_C99_SPECIFIERS_BUG(f) \ - std::string tmp = f; \ - search_and_replace(tmp, std::string("%z"), std::string("%I")); \ - f = tmp.c_str(); +#define WORKAROUND_C99_SPECIFIERS_BUG(f) \ + std::string tmp = f; \ + search_and_replace(tmp, std::string("%z"), std::string("%I")); \ + f = tmp.c_str(); #else #define WORKAROUND_C99_SPECIFIERS_BUG(f) #endif - void println(const char* fmt,...) - { - if (_enabled) { - printf("%s", indent_str.c_str()); - va_list va; - va_start(va,fmt); - WORKAROUND_C99_SPECIFIERS_BUG(fmt); - vprintf(fmt,va); - va_end(va); - printf("\n"); - } - } - void print(const char* fmt,...) - { - if (_enabled) { - printf("%s", indent_str.c_str()); - va_list va; - va_start(va,fmt); - WORKAROUND_C99_SPECIFIERS_BUG(fmt); - vprintf(fmt,va); - va_end(va); - } - } - void attention(Loc& loc, const char* fmt,...) - { - va_list va; - va_start(va,fmt); - vwarning(loc,fmt,va); - va_end(va); - } +void println(const char *fmt, ...) { + if (_enabled) { + printf("%s", indent_str.c_str()); + va_list va; + va_start(va, fmt); + WORKAROUND_C99_SPECIFIERS_BUG(fmt); + vprintf(fmt, va); + va_end(va); + printf("\n"); + } +} +void print(const char *fmt, ...) { + if (_enabled) { + printf("%s", indent_str.c_str()); + va_list va; + va_start(va, fmt); + WORKAROUND_C99_SPECIFIERS_BUG(fmt); + vprintf(fmt, va); + va_end(va); + } +} +void attention(Loc &loc, const char *fmt, ...) { + va_list va; + va_start(va, fmt); + vwarning(loc, fmt, va); + va_end(va); +} } diff --git a/gen/logger.h b/gen/logger.h index 7642278471..3e4ef1f02c 100644 --- a/gen/logger.h +++ b/gen/logger.h @@ -19,111 +19,99 @@ #include namespace llvm { - class Type; - class Value; +class Type; +class Value; } #ifndef IS_PRINTF -# ifdef __GNUC__ -# define IS_PRINTF(FMTARG) __attribute((__format__ (__printf__, (FMTARG), (FMTARG)+1) )) -# else -# define IS_PRINTF(FMTARG) -# endif +#ifdef __GNUC__ +#define IS_PRINTF(FMTARG) \ + __attribute((__format__(__printf__, (FMTARG), (FMTARG) + 1))) +#else +#define IS_PRINTF(FMTARG) +#endif #endif struct Loc; class Stream { - std::ostream* OS; + std::ostream *OS; public: - Stream() : OS(0) {} - Stream(std::ostream* S) : OS(S) {} - Stream(std::ostream& S) : OS(&S) {} + Stream() : OS(0) {} + Stream(std::ostream *S) : OS(S) {} + Stream(std::ostream &S) : OS(&S) {} - /* - Stream operator << (std::ios_base &(*Func)(std::ios_base&)) { - if (OS) *OS << Func; - return *this; - } - */ + /* + Stream operator << (std::ios_base &(*Func)(std::ios_base&)) { + if (OS) *OS << Func; + return *this; + } + */ - Stream operator << (std::ostream &(*Func)(std::ostream&)) { - if (OS) Func(*OS); - return *this; - } + Stream operator<<(std::ostream &(*Func)(std::ostream &)) { + if (OS) + Func(*OS); + return *this; + } - template - Stream& operator << (const Ty& Thing) { - if (OS) - Writer::write(*OS, Thing); - return *this; - } + template Stream &operator<<(const Ty &Thing) { + if (OS) + Writer::write(*OS, Thing); + return *this; + } private: - // Implementation details to treat llvm::Value, llvm::Type and their - // subclasses specially (to pretty-print types). + // Implementation details to treat llvm::Value, llvm::Type and their + // subclasses specially (to pretty-print types). - static void writeType(std::ostream& OS, const llvm::Type& Ty); - static void writeValue(std::ostream& OS, const llvm::Value& Ty); + static void writeType(std::ostream &OS, const llvm::Type &Ty); + static void writeValue(std::ostream &OS, const llvm::Value &Ty); - template friend struct Writer; - // error: function template partial specialization is not allowed - // So I guess type partial specialization + member function will have to do... - template - struct Writer { - static void write(std::ostream& OS, const Ty& Thing) { - OS << Thing; - } - }; + template friend struct Writer; + // error: function template partial specialization is not allowed + // So I guess type partial specialization + member function will have to do... + template struct Writer { + static void write(std::ostream &OS, const Ty &Thing) { OS << Thing; } + }; - template - struct Writer { - static void write(std::ostream& OS, const llvm::Type& Thing) { - Stream::writeType(OS, Thing); - } - static void write(std::ostream& OS, const llvm::Value& Thing) { - Stream::writeValue(OS, Thing); - } - }; + template struct Writer { + static void write(std::ostream &OS, const llvm::Type &Thing) { + Stream::writeType(OS, Thing); + } + static void write(std::ostream &OS, const llvm::Value &Thing) { + Stream::writeValue(OS, Thing); + } + }; - // NOT IMPLEMENTED - char sfinae_bait(const llvm::Type&); - char sfinae_bait(const llvm::Value&); - short sfinae_bait(...); + // NOT IMPLEMENTED + char sfinae_bait(const llvm::Type &); + char sfinae_bait(const llvm::Value &); + short sfinae_bait(...); }; -namespace Logger -{ - extern bool _enabled; +namespace Logger { +extern bool _enabled; - void indent(); - void undent(); - Stream cout(); - void println(const char* fmt, ...) IS_PRINTF(1); - void print(const char* fmt, ...) IS_PRINTF(1); - inline void enable() { _enabled = true; } - inline void disable() { _enabled = false; } - inline bool enabled() { return _enabled; } +void indent(); +void undent(); +Stream cout(); +void println(const char *fmt, ...) IS_PRINTF(1); +void print(const char *fmt, ...) IS_PRINTF(1); +inline void enable() { _enabled = true; } +inline void disable() { _enabled = false; } +inline bool enabled() { return _enabled; } - void attention(Loc loc, const char* fmt, ...) IS_PRINTF(2); +void attention(Loc loc, const char *fmt, ...) IS_PRINTF(2); - struct LoggerScope - { - LoggerScope() - { - Logger::indent(); - } - ~LoggerScope() - { - Logger::undent(); - } - }; +struct LoggerScope { + LoggerScope() { Logger::indent(); } + ~LoggerScope() { Logger::undent(); } +}; } -#define LOG_SCOPE Logger::LoggerScope _logscope; +#define LOG_SCOPE Logger::LoggerScope _logscope; -#define IF_LOG if (Logger::enabled()) +#define IF_LOG if (Logger::enabled()) #endif - diff --git a/gen/metadata.h b/gen/metadata.h index e1c6cea221..163f0676f6 100644 --- a/gen/metadata.h +++ b/gen/metadata.h @@ -18,7 +18,7 @@ #include "llvm/IR/Metadata.h" typedef llvm::Value MDNodeField; -#define METADATA_LINKAGE_TYPE llvm::GlobalValue::WeakODRLinkage +#define METADATA_LINKAGE_TYPE llvm::GlobalValue::WeakODRLinkage // *** Metadata for TypeInfo instances *** #define TD_PREFIX "llvm.ldc.typeinfo." @@ -26,27 +26,26 @@ typedef llvm::Value MDNodeField; /// The fields in the metadata node for a TypeInfo instance. /// (Its name will be TD_PREFIX ~ ) enum TypeDataFields { - TD_TypeInfo, /// A reference toe the TypeInfo global this node is for. + TD_TypeInfo, /// A reference toe the TypeInfo global this node is for. - TD_Type, /// A value of the LLVM type corresponding to this D type + TD_Type, /// A value of the LLVM type corresponding to this D type - // Must be kept last: - TD_NumFields /// The number of fields in TypeInfo metadata + // Must be kept last: + TD_NumFields /// The number of fields in TypeInfo metadata }; - // *** Metadata for ClassInfo instances *** #define CD_PREFIX "llvm.ldc.classinfo." /// The fields in the metadata node for a ClassInfo instance. /// (Its name will be CD_PREFIX ~ ) enum ClassDataFields { - CD_BodyType, /// A value of the LLVM type corresponding to the class body. - CD_Finalize, /// True if this class (or a base class) has a destructor. - CD_CustomDelete,/// True if this class has an overridden delete operator. + CD_BodyType, /// A value of the LLVM type corresponding to the class body. + CD_Finalize, /// True if this class (or a base class) has a destructor. + CD_CustomDelete, /// True if this class has an overridden delete operator. - // Must be kept last - CD_NumFields /// The number of fields in ClassInfo metadata + // Must be kept last + CD_NumFields /// The number of fields in ClassInfo metadata }; #endif diff --git a/gen/module.cpp b/gen/module.cpp index 38ab0a649b..d9be51ac39 100644 --- a/gen/module.cpp +++ b/gen/module.cpp @@ -51,285 +51,307 @@ #include #endif -static llvm::cl::opt preservePaths("op", - llvm::cl::desc("Do not strip paths from source file"), - llvm::cl::ZeroOrMore); +static llvm::cl::opt + preservePaths("op", llvm::cl::desc("Do not strip paths from source file"), + llvm::cl::ZeroOrMore); -static llvm::cl::opt fqnNames("oq", - llvm::cl::desc("Write object files with fully qualified names"), - llvm::cl::ZeroOrMore); +static llvm::cl::opt + fqnNames("oq", + llvm::cl::desc("Write object files with fully qualified names"), + llvm::cl::ZeroOrMore); -static void check_and_add_output_file(Module* NewMod, const std::string& str) -{ - static std::map files; +static void check_and_add_output_file(Module *NewMod, const std::string &str) { + static std::map files; - auto i = files.find(str); - if (i != files.end()) { - Module* ThisMod = i->second; - error(Loc(), "Output file '%s' for module '%s' collides with previous module '%s'. See the -oq option", - str.c_str(), NewMod->toPrettyChars(), ThisMod->toPrettyChars()); - fatal(); - } - files.insert(std::make_pair(str, NewMod)); + auto i = files.find(str); + if (i != files.end()) { + Module *ThisMod = i->second; + error(Loc(), "Output file '%s' for module '%s' collides with previous " + "module '%s'. See the -oq option", + str.c_str(), NewMod->toPrettyChars(), ThisMod->toPrettyChars()); + fatal(); + } + files.insert(std::make_pair(str, NewMod)); } -void Module::buildTargetFiles(bool singleObj, bool library) -{ - if (objfile && - (!doDocComment || docfile) && - (!doHdrGen || hdrfile)) - return; +void Module::buildTargetFiles(bool singleObj, bool library) { + if (objfile && (!doDocComment || docfile) && (!doHdrGen || hdrfile)) + return; - if (!objfile) { - const char *objname = library ? 0 : global.params.objname; - if (global.params.output_o) - objfile = Module::buildFilePath(objname, global.params.objdir, - global.params.targetTriple.isOSWindows() ? global.obj_ext_alt : global.obj_ext); - else if (global.params.output_bc) - objfile = Module::buildFilePath(objname, global.params.objdir, global.bc_ext); - else if (global.params.output_ll) - objfile = Module::buildFilePath(objname, global.params.objdir, global.ll_ext); - else if (global.params.output_s) - objfile = Module::buildFilePath(objname, global.params.objdir, global.s_ext); - } - if (doDocComment && !docfile) - docfile = Module::buildFilePath(global.params.docname, global.params.docdir, global.doc_ext); - if (doHdrGen && !hdrfile) - hdrfile = Module::buildFilePath(global.params.hdrname, global.params.hdrdir, global.hdr_ext); + if (!objfile) { + const char *objname = library ? 0 : global.params.objname; + if (global.params.output_o) + objfile = Module::buildFilePath(objname, global.params.objdir, + global.params.targetTriple.isOSWindows() + ? global.obj_ext_alt + : global.obj_ext); + else if (global.params.output_bc) + objfile = + Module::buildFilePath(objname, global.params.objdir, global.bc_ext); + else if (global.params.output_ll) + objfile = + Module::buildFilePath(objname, global.params.objdir, global.ll_ext); + else if (global.params.output_s) + objfile = + Module::buildFilePath(objname, global.params.objdir, global.s_ext); + } + if (doDocComment && !docfile) + docfile = Module::buildFilePath(global.params.docname, global.params.docdir, + global.doc_ext); + if (doHdrGen && !hdrfile) + hdrfile = Module::buildFilePath(global.params.hdrname, global.params.hdrdir, + global.hdr_ext); - // safety check: never allow obj, doc or hdr file to have the source file's name - if (Port::stricmp(FileName::name(objfile->name->str), FileName::name(this->arg)) == 0) { - error("Output object files with the same name as the source file are forbidden"); - fatal(); - } - if (docfile && Port::stricmp(FileName::name(docfile->name->str), FileName::name(this->arg)) == 0) { - error("Output doc files with the same name as the source file are forbidden"); - fatal(); - } - if (hdrfile && Port::stricmp(FileName::name(hdrfile->name->str), FileName::name(this->arg)) == 0) { - error("Output header files with the same name as the source file are forbidden"); - fatal(); - } + // safety check: never allow obj, doc or hdr file to have the source file's + // name + if (Port::stricmp(FileName::name(objfile->name->str), + FileName::name(this->arg)) == 0) { + error("Output object files with the same name as the source file are " + "forbidden"); + fatal(); + } + if (docfile && + Port::stricmp(FileName::name(docfile->name->str), + FileName::name(this->arg)) == 0) { + error( + "Output doc files with the same name as the source file are forbidden"); + fatal(); + } + if (hdrfile && + Port::stricmp(FileName::name(hdrfile->name->str), + FileName::name(this->arg)) == 0) { + error("Output header files with the same name as the source file are " + "forbidden"); + fatal(); + } - // LDC - // another safety check to make sure we don't overwrite previous output files - if (!singleObj && global.params.obj) - check_and_add_output_file(this, objfile->name->str); - if (docfile) - check_and_add_output_file(this, docfile->name->str); - //FIXME: DMD overwrites header files. This should be done only in a DMD mode. - //if (hdrfile) - // check_and_add_output_file(this, hdrfile->name->str); + // LDC + // another safety check to make sure we don't overwrite previous output files + if (!singleObj && global.params.obj) + check_and_add_output_file(this, objfile->name->str); + if (docfile) + check_and_add_output_file(this, docfile->name->str); + // FIXME: DMD overwrites header files. This should be done only in a DMD mode. + // if (hdrfile) + // check_and_add_output_file(this, hdrfile->name->str); } -File* Module::buildFilePath(const char* forcename, const char* path, const char* ext) -{ - const char *argobj; - if (forcename) { - argobj = forcename; - } else { - if (preservePaths) - argobj = this->arg; - else - argobj = FileName::name(this->arg); +File *Module::buildFilePath(const char *forcename, const char *path, + const char *ext) { + const char *argobj; + if (forcename) { + argobj = forcename; + } else { + if (preservePaths) + argobj = this->arg; + else + argobj = FileName::name(this->arg); - if (fqnNames) { - char *name = md ? md->toChars() : toChars(); - argobj = FileName::replaceName(argobj, name); + if (fqnNames) { + char *name = md ? md->toChars() : toChars(); + argobj = FileName::replaceName(argobj, name); - // add ext, otherwise forceExt will make nested.module into nested.bc - size_t len = strlen(argobj); - size_t extlen = strlen(ext); - char* s = (char *)alloca(len + 1 + extlen + 1); - memcpy(s, argobj, len); - s[len] = '.'; - memcpy(s + len + 1, ext, extlen + 1); - s[len+1+extlen] = 0; - argobj = s; - } + // add ext, otherwise forceExt will make nested.module into nested.bc + size_t len = strlen(argobj); + size_t extlen = strlen(ext); + char *s = (char *)alloca(len + 1 + extlen + 1); + memcpy(s, argobj, len); + s[len] = '.'; + memcpy(s + len + 1, ext, extlen + 1); + s[len + 1 + extlen] = 0; + argobj = s; } + } - if (!FileName::absolute(argobj)) - argobj = FileName::combine(path, argobj); + if (!FileName::absolute(argobj)) + argobj = FileName::combine(path, argobj); - FileName::ensurePathExists(FileName::path(argobj)); + FileName::ensurePathExists(FileName::path(argobj)); - // always append the extension! otherwise hard to make output switches consistent - return new File(FileName::forceExt(argobj, ext)); + // always append the extension! otherwise hard to make output switches + // consistent + return new File(FileName::forceExt(argobj, ext)); } -static llvm::Function* build_module_function(const std::string &name, const std::list &funcs, - const std::list &gates = std::list()) -{ - if (gates.empty()) { - if (funcs.empty()) - return NULL; +static llvm::Function *build_module_function( + const std::string &name, const std::list &funcs, + const std::list &gates = std::list()) { + if (gates.empty()) { + if (funcs.empty()) + return NULL; - if (funcs.size() == 1) - return getIrFunc(funcs.front())->func; - } + if (funcs.size() == 1) + return getIrFunc(funcs.front())->func; + } - // build ctor type - LLFunctionType* fnTy = LLFunctionType::get(LLType::getVoidTy(gIR->context()), std::vector(), false); + // build ctor type + LLFunctionType *fnTy = LLFunctionType::get(LLType::getVoidTy(gIR->context()), + std::vector(), false); - std::string const symbolName = gABI->mangleForLLVM(name, LINKd); - assert(gIR->module.getFunction(symbolName) == NULL); - llvm::Function* fn = llvm::Function::Create(fnTy, - llvm::GlobalValue::InternalLinkage, symbolName, &gIR->module); - fn->setCallingConv(gABI->callingConv(fn->getFunctionType(), LINKd)); + std::string const symbolName = gABI->mangleForLLVM(name, LINKd); + assert(gIR->module.getFunction(symbolName) == NULL); + llvm::Function *fn = llvm::Function::Create( + fnTy, llvm::GlobalValue::InternalLinkage, symbolName, &gIR->module); + fn->setCallingConv(gABI->callingConv(fn->getFunctionType(), LINKd)); - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "", fn); - IRBuilder<> builder(bb); + llvm::BasicBlock *bb = llvm::BasicBlock::Create(gIR->context(), "", fn); + IRBuilder<> builder(bb); - // debug info - ldc::DISubprogram dis = gIR->DBuilder.EmitModuleCTor(fn, name.c_str()); - if (global.params.symdebug) { - // Need _some_ debug info to avoid inliner bug, see GitHub issue #998. - builder.SetCurrentDebugLocation(llvm::DebugLoc::get(0, 0, dis)); - } + // debug info + ldc::DISubprogram dis = gIR->DBuilder.EmitModuleCTor(fn, name.c_str()); + if (global.params.symdebug) { + // Need _some_ debug info to avoid inliner bug, see GitHub issue #998. + builder.SetCurrentDebugLocation(llvm::DebugLoc::get(0, 0, dis)); + } - // Call ctor's - for (auto func : funcs) { - llvm::Function* f = getIrFunc(func)->func; + // Call ctor's + for (auto func : funcs) { + llvm::Function *f = getIrFunc(func)->func; #if LDC_LLVM_VER >= 307 - llvm::CallInst* call = builder.CreateCall(f, {}); + llvm::CallInst *call = builder.CreateCall(f, {}); #else - llvm::CallInst* call = builder.CreateCall(f, ""); + llvm::CallInst *call = builder.CreateCall(f, ""); #endif - call->setCallingConv(gABI->callingConv(call-> + call->setCallingConv(gABI->callingConv(call-> #if LDC_LLVM_VER < 307 - getCalledFunction()-> + getCalledFunction() + -> #endif - getFunctionType(), LINKd)); - } + getFunctionType(), + LINKd)); + } - // Increment vgate's - for (auto gate : gates) { - assert(getIrGlobal(gate)); - llvm::Value* val = getIrGlobal(gate)->value; - llvm::Value* rval = builder.CreateLoad(val, "vgate"); - llvm::Value* res = builder.CreateAdd(rval, DtoConstUint(1), "vgate"); - builder.CreateStore(res, val); - } + // Increment vgate's + for (auto gate : gates) { + assert(getIrGlobal(gate)); + llvm::Value *val = getIrGlobal(gate)->value; + llvm::Value *rval = builder.CreateLoad(val, "vgate"); + llvm::Value *res = builder.CreateAdd(rval, DtoConstUint(1), "vgate"); + builder.CreateStore(res, val); + } - builder.CreateRetVoid(); - return fn; + builder.CreateRetVoid(); + return fn; } // build module ctor -static llvm::Function* build_module_ctor(Module *m) -{ - std::string name("_D"); - name.append(mangle(m)); - name.append("6__ctorZ"); - IrModule *irm = getIrModule(m); - return build_module_function(name, irm->ctors, irm->gates); +static llvm::Function *build_module_ctor(Module *m) { + std::string name("_D"); + name.append(mangle(m)); + name.append("6__ctorZ"); + IrModule *irm = getIrModule(m); + return build_module_function(name, irm->ctors, irm->gates); } // build module dtor -static llvm::Function* build_module_dtor(Module *m) -{ - std::string name("_D"); - name.append(mangle(m)); - name.append("6__dtorZ"); - return build_module_function(name, getIrModule(m)->dtors); +static llvm::Function *build_module_dtor(Module *m) { + std::string name("_D"); + name.append(mangle(m)); + name.append("6__dtorZ"); + return build_module_function(name, getIrModule(m)->dtors); } // build module unittest -static llvm::Function* build_module_unittest(Module *m) -{ - std::string name("_D"); - name.append(mangle(m)); - name.append("10__unittestZ"); - return build_module_function(name, getIrModule(m)->unitTests); +static llvm::Function *build_module_unittest(Module *m) { + std::string name("_D"); + name.append(mangle(m)); + name.append("10__unittestZ"); + return build_module_function(name, getIrModule(m)->unitTests); } // build module shared ctor -static llvm::Function* build_module_shared_ctor(Module *m) -{ - std::string name("_D"); - name.append(mangle(m)); - name.append("13__shared_ctorZ"); - IrModule *irm = getIrModule(m); - return build_module_function(name, irm->sharedCtors, irm->sharedGates); +static llvm::Function *build_module_shared_ctor(Module *m) { + std::string name("_D"); + name.append(mangle(m)); + name.append("13__shared_ctorZ"); + IrModule *irm = getIrModule(m); + return build_module_function(name, irm->sharedCtors, irm->sharedGates); } // build module shared dtor -static llvm::Function* build_module_shared_dtor(Module *m) -{ - std::string name("_D"); - name.append(mangle(m)); - name.append("13__shared_dtorZ"); - return build_module_function(name, getIrModule(m)->sharedDtors); +static llvm::Function *build_module_shared_dtor(Module *m) { + std::string name("_D"); + name.append(mangle(m)); + name.append("13__shared_dtorZ"); + return build_module_function(name, getIrModule(m)->sharedDtors); } -// build ModuleReference and register function, to register the module info in the global linked list -static LLFunction* build_module_reference_and_ctor(const char *moduleMangle, LLConstant* moduleinfo) -{ - // build ctor type - LLFunctionType* fty = LLFunctionType::get(LLType::getVoidTy(gIR->context()), std::vector(), false); +// build ModuleReference and register function, to register the module info in +// the global linked list +static LLFunction *build_module_reference_and_ctor(const char *moduleMangle, + LLConstant *moduleinfo) { + // build ctor type + LLFunctionType *fty = LLFunctionType::get(LLType::getVoidTy(gIR->context()), + std::vector(), false); - // build ctor name - std::string fname = "_D"; - fname += moduleMangle; - fname += "16__moduleinfoCtorZ"; + // build ctor name + std::string fname = "_D"; + fname += moduleMangle; + fname += "16__moduleinfoCtorZ"; - // build a function that registers the moduleinfo in the global moduleinfo linked list - LLFunction* ctor = LLFunction::Create(fty, LLGlobalValue::InternalLinkage, fname, &gIR->module); + // build a function that registers the moduleinfo in the global moduleinfo + // linked list + LLFunction *ctor = LLFunction::Create(fty, LLGlobalValue::InternalLinkage, + fname, &gIR->module); - // provide the default initializer - LLStructType* modulerefTy = DtoModuleReferenceType(); - LLConstant* mrefvalues[] = { - LLConstant::getNullValue(modulerefTy->getContainedType(0)), - llvm::ConstantExpr::getBitCast(moduleinfo, modulerefTy->getContainedType(1)) - }; - LLConstant* thismrefinit = LLConstantStruct::get(modulerefTy, llvm::ArrayRef(mrefvalues)); + // provide the default initializer + LLStructType *modulerefTy = DtoModuleReferenceType(); + LLConstant *mrefvalues[] = { + LLConstant::getNullValue(modulerefTy->getContainedType(0)), + llvm::ConstantExpr::getBitCast(moduleinfo, + modulerefTy->getContainedType(1))}; + LLConstant *thismrefinit = LLConstantStruct::get( + modulerefTy, llvm::ArrayRef(mrefvalues)); - // create the ModuleReference node for this module - std::string thismrefname = "_D"; - thismrefname += moduleMangle; - thismrefname += "11__moduleRefZ"; - Loc loc; - LLGlobalVariable* thismref = getOrCreateGlobal(loc, gIR->module, - modulerefTy, false, LLGlobalValue::InternalLinkage, thismrefinit, - thismrefname); - // make sure _Dmodule_ref is declared - LLConstant* mref = gIR->module.getNamedGlobal("_Dmodule_ref"); - LLType *modulerefPtrTy = getPtrToType(modulerefTy); - if (!mref) - mref = new LLGlobalVariable(gIR->module, modulerefPtrTy, false, LLGlobalValue::ExternalLinkage, NULL, "_Dmodule_ref"); - mref = DtoBitCast(mref, getPtrToType(modulerefPtrTy)); + // create the ModuleReference node for this module + std::string thismrefname = "_D"; + thismrefname += moduleMangle; + thismrefname += "11__moduleRefZ"; + Loc loc; + LLGlobalVariable *thismref = getOrCreateGlobal( + loc, gIR->module, modulerefTy, false, LLGlobalValue::InternalLinkage, + thismrefinit, thismrefname); + // make sure _Dmodule_ref is declared + LLConstant *mref = gIR->module.getNamedGlobal("_Dmodule_ref"); + LLType *modulerefPtrTy = getPtrToType(modulerefTy); + if (!mref) + mref = new LLGlobalVariable(gIR->module, modulerefPtrTy, false, + LLGlobalValue::ExternalLinkage, NULL, + "_Dmodule_ref"); + mref = DtoBitCast(mref, getPtrToType(modulerefPtrTy)); - // make the function insert this moduleinfo as the beginning of the _Dmodule_ref linked list - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "moduleinfoCtorEntry", ctor); - IRBuilder<> builder(bb); + // make the function insert this moduleinfo as the beginning of the + // _Dmodule_ref linked list + llvm::BasicBlock *bb = + llvm::BasicBlock::Create(gIR->context(), "moduleinfoCtorEntry", ctor); + IRBuilder<> builder(bb); - // debug info - gIR->DBuilder.EmitModuleCTor(ctor, fname.c_str()); + // debug info + gIR->DBuilder.EmitModuleCTor(ctor, fname.c_str()); - // get current beginning - LLValue* curbeg = builder.CreateLoad(mref, "current"); + // get current beginning + LLValue *curbeg = builder.CreateLoad(mref, "current"); - // put current beginning as the next of this one - LLValue* gep = builder.CreateStructGEP( + // put current beginning as the next of this one + LLValue *gep = builder.CreateStructGEP( #if LDC_LLVM_VER >= 307 - modulerefTy, + modulerefTy, #endif - thismref, 0, "next"); - builder.CreateStore(curbeg, gep); + thismref, 0, "next"); + builder.CreateStore(curbeg, gep); - // replace beginning - builder.CreateStore(thismref, mref); + // replace beginning + builder.CreateStore(thismref, mref); - // return - builder.CreateRetVoid(); + // return + builder.CreateRetVoid(); - return ctor; + return ctor; } /// Builds the body for the ldc.dso_ctor and ldc.dso_dtor functions. @@ -341,584 +363,559 @@ static LLFunction* build_module_reference_and_ctor(const char *moduleMangle, LLC /// _d_dso_registry(cast(CompilerDSOData*)&record); /// } static void build_dso_ctor_dtor_body( - llvm::Function* targetFunc, - llvm::Value* dsoInitialized, - llvm::Value* dsoSlot, - llvm::Value* minfoBeg, - llvm::Value* minfoEnd, - llvm::Value* minfoUsedPointer, - bool executeWhenInitialized -) { - llvm::Function* const dsoRegistry = LLVM_D_GetRuntimeFunction(Loc(), - gIR->module, "_d_dso_registry"); - llvm::Type* const recordPtrTy = dsoRegistry->getFunctionType()->getContainedType(1); + llvm::Function *targetFunc, llvm::Value *dsoInitialized, + llvm::Value *dsoSlot, llvm::Value *minfoBeg, llvm::Value *minfoEnd, + llvm::Value *minfoUsedPointer, bool executeWhenInitialized) { + llvm::Function *const dsoRegistry = + LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_dso_registry"); + llvm::Type *const recordPtrTy = + dsoRegistry->getFunctionType()->getContainedType(1); - llvm::BasicBlock* const entryBB = - llvm::BasicBlock::Create(gIR->context(), "", targetFunc); - llvm::BasicBlock* const initBB = - llvm::BasicBlock::Create(gIR->context(), "init", targetFunc); - llvm::BasicBlock* const endBB = - llvm::BasicBlock::Create(gIR->context(), "end", targetFunc); + llvm::BasicBlock *const entryBB = + llvm::BasicBlock::Create(gIR->context(), "", targetFunc); + llvm::BasicBlock *const initBB = + llvm::BasicBlock::Create(gIR->context(), "init", targetFunc); + llvm::BasicBlock *const endBB = + llvm::BasicBlock::Create(gIR->context(), "end", targetFunc); - { - IRBuilder<> b(entryBB); - llvm::Value* condEval = b.CreateICmp(executeWhenInitialized ? llvm::ICmpInst::ICMP_NE - : llvm::ICmpInst::ICMP_EQ, - b.CreateLoad(dsoInitialized), - b.getInt8(0)); - b.CreateCondBr(condEval, initBB, endBB); - } - { - IRBuilder<> b(initBB); - b.CreateStore(b.getInt8(!executeWhenInitialized), dsoInitialized); + { + IRBuilder<> b(entryBB); + llvm::Value *condEval = + b.CreateICmp(executeWhenInitialized ? llvm::ICmpInst::ICMP_NE + : llvm::ICmpInst::ICMP_EQ, + b.CreateLoad(dsoInitialized), b.getInt8(0)); + b.CreateCondBr(condEval, initBB, endBB); + } + { + IRBuilder<> b(initBB); + b.CreateStore(b.getInt8(!executeWhenInitialized), dsoInitialized); - llvm::Constant* version = DtoConstSize_t(1); - llvm::Type* memberTypes[] = { - version->getType(), - dsoSlot->getType(), - minfoBeg->getType(), - minfoEnd->getType(), - minfoUsedPointer->getType() - }; - llvm::StructType* stype = llvm::StructType::get(gIR->context(), memberTypes, false); - llvm::Value* record = b.CreateAlloca(stype); + llvm::Constant *version = DtoConstSize_t(1); + llvm::Type *memberTypes[] = {version->getType(), dsoSlot->getType(), + minfoBeg->getType(), minfoEnd->getType(), + minfoUsedPointer->getType()}; + llvm::StructType *stype = + llvm::StructType::get(gIR->context(), memberTypes, false); + llvm::Value *record = b.CreateAlloca(stype); #if LDC_LLVM_VER >= 307 - b.CreateStore(version, b.CreateStructGEP(stype, record, 0)); // version - b.CreateStore(dsoSlot, b.CreateStructGEP(stype, record, 1)); // slot - b.CreateStore(minfoBeg, b.CreateStructGEP(stype, record, 2)); - b.CreateStore(minfoEnd, b.CreateStructGEP(stype, record, 3)); - b.CreateStore(minfoUsedPointer, b.CreateStructGEP(stype, record, 4)); + b.CreateStore(version, b.CreateStructGEP(stype, record, 0)); // version + b.CreateStore(dsoSlot, b.CreateStructGEP(stype, record, 1)); // slot + b.CreateStore(minfoBeg, b.CreateStructGEP(stype, record, 2)); + b.CreateStore(minfoEnd, b.CreateStructGEP(stype, record, 3)); + b.CreateStore(minfoUsedPointer, b.CreateStructGEP(stype, record, 4)); #else - b.CreateStore(version, b.CreateStructGEP(record, 0)); // version - b.CreateStore(dsoSlot, b.CreateStructGEP(record, 1)); // slot - b.CreateStore(minfoBeg, b.CreateStructGEP(record, 2)); - b.CreateStore(minfoEnd, b.CreateStructGEP(record, 3)); - b.CreateStore(minfoUsedPointer, b.CreateStructGEP(record, 4)); + b.CreateStore(version, b.CreateStructGEP(record, 0)); // version + b.CreateStore(dsoSlot, b.CreateStructGEP(record, 1)); // slot + b.CreateStore(minfoBeg, b.CreateStructGEP(record, 2)); + b.CreateStore(minfoEnd, b.CreateStructGEP(record, 3)); + b.CreateStore(minfoUsedPointer, b.CreateStructGEP(record, 4)); #endif - b.CreateCall(dsoRegistry, b.CreateBitCast(record, recordPtrTy)); - b.CreateBr(endBB); - } - { - IRBuilder<> b(endBB); - b.CreateRetVoid(); - } + b.CreateCall(dsoRegistry, b.CreateBitCast(record, recordPtrTy)); + b.CreateBr(endBB); + } + { + IRBuilder<> b(endBB); + b.CreateRetVoid(); + } } -static void build_module_ref(std::string moduleMangle, llvm::Constant* thisModuleInfo) -{ - // Build the ModuleInfo reference and bracketing symbols. - llvm::Type* const moduleInfoPtrTy = DtoPtrToType(Module::moduleinfo->type); +static void build_module_ref(std::string moduleMangle, + llvm::Constant *thisModuleInfo) { + // Build the ModuleInfo reference and bracketing symbols. + llvm::Type *const moduleInfoPtrTy = DtoPtrToType(Module::moduleinfo->type); - std::string thismrefname = "_D"; - thismrefname += moduleMangle; - thismrefname += "11__moduleRefZ"; - llvm::GlobalVariable* thismref = new llvm::GlobalVariable( - gIR->module, - moduleInfoPtrTy, - false, // FIXME: mRelocModel != llvm::Reloc::PIC_ - llvm::GlobalValue::LinkOnceODRLinkage, - DtoBitCast(thisModuleInfo, moduleInfoPtrTy), - thismrefname - ); - thismref->setSection(".minfo"); - gIR->usedArray.push_back(thismref); + std::string thismrefname = "_D"; + thismrefname += moduleMangle; + thismrefname += "11__moduleRefZ"; + llvm::GlobalVariable *thismref = new llvm::GlobalVariable( + gIR->module, moduleInfoPtrTy, + false, // FIXME: mRelocModel != llvm::Reloc::PIC_ + llvm::GlobalValue::LinkOnceODRLinkage, + DtoBitCast(thisModuleInfo, moduleInfoPtrTy), thismrefname); + thismref->setSection(".minfo"); + gIR->usedArray.push_back(thismref); } -static void build_dso_registry_calls(std::string moduleMangle, llvm::Constant* thisModuleInfo) -{ - // Build the ModuleInfo reference and bracketing symbols. - llvm::Type* const moduleInfoPtrTy = DtoPtrToType(Module::moduleinfo->type); +static void build_dso_registry_calls(std::string moduleMangle, + llvm::Constant *thisModuleInfo) { + // Build the ModuleInfo reference and bracketing symbols. + llvm::Type *const moduleInfoPtrTy = DtoPtrToType(Module::moduleinfo->type); - // Order is important here: We must create the symbols in the - // bracketing sections right before/after the ModuleInfo reference - // so that they end up in the correct order in the object file. - llvm::GlobalVariable* minfoBeg = new llvm::GlobalVariable( - gIR->module, - moduleInfoPtrTy, - false, // FIXME: mRelocModel != llvm::Reloc::PIC_ - llvm::GlobalValue::LinkOnceODRLinkage, - getNullPtr(moduleInfoPtrTy), - "_minfo_beg" - ); - minfoBeg->setSection(".minfo_beg"); - minfoBeg->setVisibility(llvm::GlobalValue::HiddenVisibility); + // Order is important here: We must create the symbols in the + // bracketing sections right before/after the ModuleInfo reference + // so that they end up in the correct order in the object file. + llvm::GlobalVariable *minfoBeg = + new llvm::GlobalVariable(gIR->module, moduleInfoPtrTy, + false, // FIXME: mRelocModel != llvm::Reloc::PIC_ + llvm::GlobalValue::LinkOnceODRLinkage, + getNullPtr(moduleInfoPtrTy), "_minfo_beg"); + minfoBeg->setSection(".minfo_beg"); + minfoBeg->setVisibility(llvm::GlobalValue::HiddenVisibility); - std::string thismrefname = "_D"; - thismrefname += moduleMangle; - thismrefname += "11__moduleRefZ"; - llvm::GlobalVariable* thismref = new llvm::GlobalVariable( - gIR->module, - moduleInfoPtrTy, - false, // FIXME: mRelocModel != llvm::Reloc::PIC_ - llvm::GlobalValue::LinkOnceODRLinkage, - DtoBitCast(thisModuleInfo, moduleInfoPtrTy), - thismrefname - ); - thismref->setSection(".minfo"); - gIR->usedArray.push_back(thismref); + std::string thismrefname = "_D"; + thismrefname += moduleMangle; + thismrefname += "11__moduleRefZ"; + llvm::GlobalVariable *thismref = new llvm::GlobalVariable( + gIR->module, moduleInfoPtrTy, + false, // FIXME: mRelocModel != llvm::Reloc::PIC_ + llvm::GlobalValue::LinkOnceODRLinkage, + DtoBitCast(thisModuleInfo, moduleInfoPtrTy), thismrefname); + thismref->setSection(".minfo"); + gIR->usedArray.push_back(thismref); - llvm::GlobalVariable* minfoEnd = new llvm::GlobalVariable( - gIR->module, - moduleInfoPtrTy, - false, // FIXME: mRelocModel != llvm::Reloc::PIC_ - llvm::GlobalValue::LinkOnceODRLinkage, - getNullPtr(moduleInfoPtrTy), - "_minfo_end" - ); - minfoEnd->setSection(".minfo_end"); - minfoEnd->setVisibility(llvm::GlobalValue::HiddenVisibility); + llvm::GlobalVariable *minfoEnd = + new llvm::GlobalVariable(gIR->module, moduleInfoPtrTy, + false, // FIXME: mRelocModel != llvm::Reloc::PIC_ + llvm::GlobalValue::LinkOnceODRLinkage, + getNullPtr(moduleInfoPtrTy), "_minfo_end"); + minfoEnd->setSection(".minfo_end"); + minfoEnd->setVisibility(llvm::GlobalValue::HiddenVisibility); - // Build the ctor to invoke _d_dso_registry. + // Build the ctor to invoke _d_dso_registry. - // This is the DSO slot for use by the druntime implementation. - llvm::GlobalVariable* dsoSlot = new llvm::GlobalVariable( - gIR->module, - getVoidPtrType(), - false, - llvm::GlobalValue::LinkOnceODRLinkage, - getNullPtr(getVoidPtrType()), - "ldc.dso_slot" - ); - dsoSlot->setVisibility(llvm::GlobalValue::HiddenVisibility); + // This is the DSO slot for use by the druntime implementation. + llvm::GlobalVariable *dsoSlot = + new llvm::GlobalVariable(gIR->module, getVoidPtrType(), false, + llvm::GlobalValue::LinkOnceODRLinkage, + getNullPtr(getVoidPtrType()), "ldc.dso_slot"); + dsoSlot->setVisibility(llvm::GlobalValue::HiddenVisibility); - // Okay, so the theory is easy: We want to have one global constructor and - // destructor per object (i.e. executable/shared library) that calls - // _d_dso_registry with the respective DSO record. However, there are a - // couple of issues that make this harder than necessary: - // - // 1) The natural way to implement the "one-per-image" part would be to - // emit a weak reference to a weak function into a .ctors. - // section (llvm.global_ctors doesn't support the necessary - // functionality, so we'd use our knowledge of the linker script to work - // around that). But as of LLVM 3.4, emitting a symbol both as weak and - // into a custom section is not supported by the MC layer. Thus, we have - // to use a normal ctor/dtor and manually ensure that we only perform - // the call once. This is done by introducing ldc.dso_initialized. - // - // 2) To make sure the .minfo section isn't removed by the linker when - // using --gc-sections, we need to keep a reference to it around in - // _every_ object file (as --gc-sections works per object file). The - // natural place for this is the ctor, where we just load a reference - // on the stack after the DSO record (to ensure LLVM doesn't optimize - // it out). However, this way, we need to have at least one ctor - // instance per object file be pulled into the final executable. We - // do this here by making the module mangle string part of its name, - // even thoguht this is slightly wasteful on -singleobj builds. - // - // It might be a better idea to simply use a custom linker script (using - // INSERT AFTER… so as to still keep the default one) to avoid all these - // problems. This would mean that it is no longer safe to link D objects - // directly using e.g. "g++ dcode.o cppcode.o", though. + // Okay, so the theory is easy: We want to have one global constructor and + // destructor per object (i.e. executable/shared library) that calls + // _d_dso_registry with the respective DSO record. However, there are a + // couple of issues that make this harder than necessary: + // + // 1) The natural way to implement the "one-per-image" part would be to + // emit a weak reference to a weak function into a .ctors. + // section (llvm.global_ctors doesn't support the necessary + // functionality, so we'd use our knowledge of the linker script to work + // around that). But as of LLVM 3.4, emitting a symbol both as weak and + // into a custom section is not supported by the MC layer. Thus, we have + // to use a normal ctor/dtor and manually ensure that we only perform + // the call once. This is done by introducing ldc.dso_initialized. + // + // 2) To make sure the .minfo section isn't removed by the linker when + // using --gc-sections, we need to keep a reference to it around in + // _every_ object file (as --gc-sections works per object file). The + // natural place for this is the ctor, where we just load a reference + // on the stack after the DSO record (to ensure LLVM doesn't optimize + // it out). However, this way, we need to have at least one ctor + // instance per object file be pulled into the final executable. We + // do this here by making the module mangle string part of its name, + // even thoguht this is slightly wasteful on -singleobj builds. + // + // It might be a better idea to simply use a custom linker script (using + // INSERT AFTER… so as to still keep the default one) to avoid all these + // problems. This would mean that it is no longer safe to link D objects + // directly using e.g. "g++ dcode.o cppcode.o", though. - llvm::GlobalVariable* dsoInitialized = new llvm::GlobalVariable( - gIR->module, - llvm::Type::getInt8Ty(gIR->context()), - false, - llvm::GlobalValue::LinkOnceODRLinkage, - llvm::ConstantInt::get(llvm::Type::getInt8Ty(gIR->context()), 0), - "ldc.dso_initialized" - ); - dsoInitialized->setVisibility(llvm::GlobalValue::HiddenVisibility); + llvm::GlobalVariable *dsoInitialized = new llvm::GlobalVariable( + gIR->module, llvm::Type::getInt8Ty(gIR->context()), false, + llvm::GlobalValue::LinkOnceODRLinkage, + llvm::ConstantInt::get(llvm::Type::getInt8Ty(gIR->context()), 0), + "ldc.dso_initialized"); + dsoInitialized->setVisibility(llvm::GlobalValue::HiddenVisibility); - // There is no reason for this cast to void*, other than that removing it - // seems to trigger a bug in the llvm::Linker (at least on LLVM 3.4) - // causing it to not merge the %object.ModuleInfo types properly. This - // manifests itself in a type mismatch assertion being triggered on the - // minfoUsedPointer store in the ctor as soon as the optimizer runs. - llvm::Value* minfoRefPtr = DtoBitCast(thismref, getVoidPtrType()); + // There is no reason for this cast to void*, other than that removing it + // seems to trigger a bug in the llvm::Linker (at least on LLVM 3.4) + // causing it to not merge the %object.ModuleInfo types properly. This + // manifests itself in a type mismatch assertion being triggered on the + // minfoUsedPointer store in the ctor as soon as the optimizer runs. + llvm::Value *minfoRefPtr = DtoBitCast(thismref, getVoidPtrType()); - std::string ctorName = "ldc.dso_ctor."; - ctorName += moduleMangle; - llvm::Function* dsoCtor = llvm::Function::Create( - llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false), - llvm::GlobalValue::LinkOnceODRLinkage, - ctorName, - &gIR->module - ); - dsoCtor->setVisibility(llvm::GlobalValue::HiddenVisibility); - build_dso_ctor_dtor_body(dsoCtor, dsoInitialized, dsoSlot, minfoBeg, minfoEnd, minfoRefPtr, false); - llvm::appendToGlobalCtors(gIR->module, dsoCtor, 65535); + std::string ctorName = "ldc.dso_ctor."; + ctorName += moduleMangle; + llvm::Function *dsoCtor = llvm::Function::Create( + llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false), + llvm::GlobalValue::LinkOnceODRLinkage, ctorName, &gIR->module); + dsoCtor->setVisibility(llvm::GlobalValue::HiddenVisibility); + build_dso_ctor_dtor_body(dsoCtor, dsoInitialized, dsoSlot, minfoBeg, minfoEnd, + minfoRefPtr, false); + llvm::appendToGlobalCtors(gIR->module, dsoCtor, 65535); - std::string dtorName = "ldc.dso_dtor."; - dtorName += moduleMangle; - llvm::Function* dsoDtor = llvm::Function::Create( - llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false), - llvm::GlobalValue::LinkOnceODRLinkage, - dtorName, - &gIR->module - ); - dsoDtor->setVisibility(llvm::GlobalValue::HiddenVisibility); - build_dso_ctor_dtor_body(dsoDtor, dsoInitialized, dsoSlot, minfoBeg, minfoEnd, minfoRefPtr, true); - llvm::appendToGlobalDtors(gIR->module, dsoDtor, 65535); + std::string dtorName = "ldc.dso_dtor."; + dtorName += moduleMangle; + llvm::Function *dsoDtor = llvm::Function::Create( + llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false), + llvm::GlobalValue::LinkOnceODRLinkage, dtorName, &gIR->module); + dsoDtor->setVisibility(llvm::GlobalValue::HiddenVisibility); + build_dso_ctor_dtor_body(dsoDtor, dsoInitialized, dsoSlot, minfoBeg, minfoEnd, + minfoRefPtr, true); + llvm::appendToGlobalDtors(gIR->module, dsoDtor, 65535); } -static void build_llvm_used_array(IRState* p) -{ - if (p->usedArray.empty()) return; +static void build_llvm_used_array(IRState *p) { + if (p->usedArray.empty()) + return; - std::vector usedVoidPtrs; - usedVoidPtrs.reserve(p->usedArray.size()); + std::vector usedVoidPtrs; + usedVoidPtrs.reserve(p->usedArray.size()); - for (auto constant : p->usedArray) - usedVoidPtrs.push_back(DtoBitCast(constant, getVoidPtrType())); + for (auto constant : p->usedArray) + usedVoidPtrs.push_back(DtoBitCast(constant, getVoidPtrType())); - llvm::ArrayType *arrayType = llvm::ArrayType::get( - getVoidPtrType(), usedVoidPtrs.size()); - llvm::GlobalVariable* llvmUsed = new llvm::GlobalVariable( - p->module, - arrayType, - false, - llvm::GlobalValue::AppendingLinkage, - llvm::ConstantArray::get(arrayType, usedVoidPtrs), - "llvm.used" - ); - llvmUsed->setSection("llvm.metadata"); + llvm::ArrayType *arrayType = + llvm::ArrayType::get(getVoidPtrType(), usedVoidPtrs.size()); + llvm::GlobalVariable *llvmUsed = new llvm::GlobalVariable( + p->module, arrayType, false, llvm::GlobalValue::AppendingLinkage, + llvm::ConstantArray::get(arrayType, usedVoidPtrs), "llvm.used"); + llvmUsed->setSection("llvm.metadata"); } // Add module-private variables and functions for coverage analysis. -static void addCoverageAnalysis(Module* m) -{ - IF_LOG { - Logger::println("Adding coverage analysis for module %s (%d lines)", m->srcfile->toChars(), m->numlines); - Logger::indent(); - } +static void addCoverageAnalysis(Module *m) { + IF_LOG { + Logger::println("Adding coverage analysis for module %s (%d lines)", + m->srcfile->toChars(), m->numlines); + Logger::indent(); + } - // size_t[# source lines / # bits in sizeTy] _d_cover_valid - LLValue* d_cover_valid_slice = NULL; - { - unsigned Dsizet_bits = gDataLayout->getTypeSizeInBits(DtoSize_t()); - size_t array_size = (m->numlines + (Dsizet_bits-1)) / Dsizet_bits; // ceil + // size_t[# source lines / # bits in sizeTy] _d_cover_valid + LLValue *d_cover_valid_slice = NULL; + { + unsigned Dsizet_bits = gDataLayout->getTypeSizeInBits(DtoSize_t()); + size_t array_size = (m->numlines + (Dsizet_bits - 1)) / Dsizet_bits; // ceil - // Work around a bug in the interface of druntime's _d_cover_register2 - // https://issues.dlang.org/show_bug.cgi?id=14417 - // For safety, make the array large enough such that the slice passed to _d_cover_register2 is completely valid. - array_size = m->numlines; + // Work around a bug in the interface of druntime's _d_cover_register2 + // https://issues.dlang.org/show_bug.cgi?id=14417 + // For safety, make the array large enough such that the slice passed to + // _d_cover_register2 is completely valid. + array_size = m->numlines; - IF_LOG Logger::println("Build private variable: size_t[%llu] _d_cover_valid", static_cast(array_size)); + IF_LOG Logger::println( + "Build private variable: size_t[%llu] _d_cover_valid", + static_cast(array_size)); - llvm::ArrayType* type = llvm::ArrayType::get(DtoSize_t(), array_size); - llvm::ConstantAggregateZero* zeroinitializer = llvm::ConstantAggregateZero::get(type); - m->d_cover_valid = new llvm::GlobalVariable(gIR->module, type, true, LLGlobalValue::InternalLinkage, zeroinitializer, "_d_cover_valid"); - LLConstant* idxs[] = { DtoConstUint(0), DtoConstUint(0) }; - d_cover_valid_slice = DtoConstSlice( DtoConstSize_t(type->getArrayNumElements()), - llvm::ConstantExpr::getGetElementPtr( + llvm::ArrayType *type = llvm::ArrayType::get(DtoSize_t(), array_size); + llvm::ConstantAggregateZero *zeroinitializer = + llvm::ConstantAggregateZero::get(type); + m->d_cover_valid = new llvm::GlobalVariable( + gIR->module, type, true, LLGlobalValue::InternalLinkage, + zeroinitializer, "_d_cover_valid"); + LLConstant *idxs[] = {DtoConstUint(0), DtoConstUint(0)}; + d_cover_valid_slice = + DtoConstSlice(DtoConstSize_t(type->getArrayNumElements()), + llvm::ConstantExpr::getGetElementPtr( #if LDC_LLVM_VER >= 307 - type, + type, #endif - m->d_cover_valid, idxs, true) ); + m->d_cover_valid, idxs, true)); - // Assert that initializer array elements have enough bits - assert(sizeof(m->d_cover_valid_init[0])*8 >= gDataLayout->getTypeSizeInBits(DtoSize_t())); - m->d_cover_valid_init.resize(array_size); - } + // Assert that initializer array elements have enough bits + assert(sizeof(m->d_cover_valid_init[0]) * 8 >= + gDataLayout->getTypeSizeInBits(DtoSize_t())); + m->d_cover_valid_init.resize(array_size); + } - // uint[# source lines] _d_cover_data - LLValue* d_cover_data_slice = NULL; - { - IF_LOG Logger::println("Build private variable: uint[%d] _d_cover_data", m->numlines); + // uint[# source lines] _d_cover_data + LLValue *d_cover_data_slice = NULL; + { + IF_LOG Logger::println("Build private variable: uint[%d] _d_cover_data", + m->numlines); - LLArrayType* type = LLArrayType::get(LLType::getInt32Ty(gIR->context()), m->numlines); - llvm::ConstantAggregateZero* zeroinitializer = llvm::ConstantAggregateZero::get(type); - m->d_cover_data = new llvm::GlobalVariable(gIR->module, type, false, LLGlobalValue::InternalLinkage, zeroinitializer, "_d_cover_data"); - LLConstant* idxs[] = { DtoConstUint(0), DtoConstUint(0) }; - d_cover_data_slice = DtoConstSlice( DtoConstSize_t(type->getArrayNumElements()), - llvm::ConstantExpr::getGetElementPtr( + LLArrayType *type = + LLArrayType::get(LLType::getInt32Ty(gIR->context()), m->numlines); + llvm::ConstantAggregateZero *zeroinitializer = + llvm::ConstantAggregateZero::get(type); + m->d_cover_data = new llvm::GlobalVariable( + gIR->module, type, false, LLGlobalValue::InternalLinkage, + zeroinitializer, "_d_cover_data"); + LLConstant *idxs[] = {DtoConstUint(0), DtoConstUint(0)}; + d_cover_data_slice = + DtoConstSlice(DtoConstSize_t(type->getArrayNumElements()), + llvm::ConstantExpr::getGetElementPtr( #if LDC_LLVM_VER >= 307 - type, + type, #endif - m->d_cover_data, idxs, true) ); + m->d_cover_data, idxs, true)); + } + + // Create "static constructor" that calls _d_cover_register2(string filename, + // size_t[] valid, uint[] data, ubyte minPercent) + // Build ctor name + LLFunction *ctor = NULL; + std::string ctorname = "_D"; + ctorname += mangle(m); + ctorname += "12_coverageanalysisCtor1FZv"; + { + IF_LOG Logger::println("Build Coverage Analysis constructor: %s", + ctorname.c_str()); + + LLFunctionType *ctorTy = LLFunctionType::get( + LLType::getVoidTy(gIR->context()), std::vector(), false); + ctor = LLFunction::Create(ctorTy, LLGlobalValue::InternalLinkage, ctorname, + &gIR->module); + ctor->setCallingConv(gABI->callingConv(ctor->getFunctionType(), LINKd)); + // Set function attributes. See functions.cpp:DtoDefineFunction() + if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) { + ctor->addFnAttr(LLAttribute::UWTable); } - // Create "static constructor" that calls _d_cover_register2(string filename, size_t[] valid, uint[] data, ubyte minPercent) - // Build ctor name - LLFunction* ctor = NULL; - std::string ctorname = "_D"; - ctorname += mangle(m); - ctorname += "12_coverageanalysisCtor1FZv"; - { - IF_LOG Logger::println("Build Coverage Analysis constructor: %s", ctorname.c_str()); + llvm::BasicBlock *bb = llvm::BasicBlock::Create(gIR->context(), "", ctor); + IRBuilder<> builder(bb); - LLFunctionType* ctorTy = LLFunctionType::get(LLType::getVoidTy(gIR->context()), std::vector(), false); - ctor = LLFunction::Create(ctorTy, LLGlobalValue::InternalLinkage, ctorname, &gIR->module); - ctor->setCallingConv(gABI->callingConv(ctor->getFunctionType(), LINKd)); - // Set function attributes. See functions.cpp:DtoDefineFunction() - if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) - { - ctor->addFnAttr(LLAttribute::UWTable); - } - - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "", ctor); - IRBuilder<> builder(bb); - - // Set up call to _d_cover_register2 - llvm::Function* fn = LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_cover_register2"); - LLValue* args[] = { - DtoConstString(m->srcfile->name->toChars()), - d_cover_valid_slice, - d_cover_data_slice, - DtoConstUbyte(global.params.covPercent) - }; - // Check if argument types are correct - for (unsigned i = 0; i < 4; ++i) { - assert(args[i]->getType() == fn->getFunctionType()->getParamType(i)); - } - - builder.CreateCall(fn, args); - - builder.CreateRetVoid(); + // Set up call to _d_cover_register2 + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_cover_register2"); + LLValue *args[] = {DtoConstString(m->srcfile->name->toChars()), + d_cover_valid_slice, d_cover_data_slice, + DtoConstUbyte(global.params.covPercent)}; + // Check if argument types are correct + for (unsigned i = 0; i < 4; ++i) { + assert(args[i]->getType() == fn->getFunctionType()->getParamType(i)); } - // Add the ctor to the module's static ctors list. TODO: This is quite the hack. - { - IF_LOG Logger::println("Add %s to module's shared static constructor list", ctorname.c_str()); - FuncDeclaration* fd = FuncDeclaration::genCfunc(NULL, Type::tvoid, ctorname.c_str()); - fd->linkage = LINKd; - IrFunction* irfunc = getIrFunc(fd, true); - irfunc->func = ctor; - getIrModule(m)->sharedCtors.push_back(fd); - } + builder.CreateCall(fn, args); - IF_LOG Logger::undent(); + builder.CreateRetVoid(); + } + + // Add the ctor to the module's static ctors list. TODO: This is quite the + // hack. + { + IF_LOG Logger::println("Add %s to module's shared static constructor list", + ctorname.c_str()); + FuncDeclaration *fd = + FuncDeclaration::genCfunc(NULL, Type::tvoid, ctorname.c_str()); + fd->linkage = LINKd; + IrFunction *irfunc = getIrFunc(fd, true); + irfunc->func = ctor; + getIrModule(m)->sharedCtors.push_back(fd); + } + + IF_LOG Logger::undent(); } // Initialize _d_cover_valid for coverage analysis -static void addCoverageAnalysisInitializer(Module* m) { - IF_LOG Logger::println("Adding coverage analysis _d_cover_valid initializer"); +static void addCoverageAnalysisInitializer(Module *m) { + IF_LOG Logger::println("Adding coverage analysis _d_cover_valid initializer"); - size_t array_size = m->d_cover_valid_init.size(); + size_t array_size = m->d_cover_valid_init.size(); - llvm::ArrayType* type = llvm::ArrayType::get(DtoSize_t(), array_size); - std::vector arrayInits(array_size); - for (size_t i=0; id_cover_valid_init[i]); - } - m->d_cover_valid->setInitializer(llvm::ConstantArray::get(type, arrayInits)); + llvm::ArrayType *type = llvm::ArrayType::get(DtoSize_t(), array_size); + std::vector arrayInits(array_size); + for (size_t i = 0; i < array_size; i++) { + arrayInits[i] = DtoConstSize_t(m->d_cover_valid_init[i]); + } + m->d_cover_valid->setInitializer(llvm::ConstantArray::get(type, arrayInits)); } static void genModuleInfo(Module *m, bool emitFullModuleInfo); -void codegenModule(IRState *irs, Module* m, bool emitFullModuleInfo) -{ - assert(!irs->dmodule && "irs->module not null, codegen already in progress?!"); - irs->dmodule = m; - assert(!gIR && "gIR not null, codegen already in progress?!"); - gIR = irs; +void codegenModule(IRState *irs, Module *m, bool emitFullModuleInfo) { + assert(!irs->dmodule && + "irs->module not null, codegen already in progress?!"); + irs->dmodule = m; + assert(!gIR && "gIR not null, codegen already in progress?!"); + gIR = irs; - LLVM_D_InitRuntime(); + LLVM_D_InitRuntime(); - // Skip pseudo-modules for coverage analysis - std::string name = m->toChars(); - if (global.params.cov && name != "__entrypoint" && name != "__main") - { - addCoverageAnalysis(m); - } + // Skip pseudo-modules for coverage analysis + std::string name = m->toChars(); + if (global.params.cov && name != "__entrypoint" && name != "__main") { + addCoverageAnalysis(m); + } - // process module members - for (unsigned k=0; k < m->members->dim; k++) - { - Dsymbol* dsym = (*m->members)[k]; - assert(dsym); - Declaration_codegen(dsym); - } + // process module members + for (unsigned k = 0; k < m->members->dim; k++) { + Dsymbol *dsym = (*m->members)[k]; + assert(dsym); + Declaration_codegen(dsym); + } - if (global.errors) fatal(); + if (global.errors) + fatal(); - // Skip emission of all the additional module metadata if requested by the user. - if (!m->noModuleInfo) - { - // generate ModuleInfo - genModuleInfo(m, emitFullModuleInfo); + // Skip emission of all the additional module metadata if requested by the + // user. + if (!m->noModuleInfo) { + // generate ModuleInfo + genModuleInfo(m, emitFullModuleInfo); - build_llvm_used_array(irs); - } + build_llvm_used_array(irs); + } - if (m->d_cover_valid) - { - addCoverageAnalysisInitializer(m); - } + if (m->d_cover_valid) { + addCoverageAnalysisInitializer(m); + } - gIR = 0; - irs->dmodule = 0; + gIR = 0; + irs->dmodule = 0; } // Put out instance of ModuleInfo for this Module -static void genModuleInfo(Module *m, bool emitFullModuleInfo) -{ - // resolve ModuleInfo - if (!Module::moduleinfo) - { - m->error("object.d is missing the ModuleInfo struct"); - fatal(); +static void genModuleInfo(Module *m, bool emitFullModuleInfo) { + // resolve ModuleInfo + if (!Module::moduleinfo) { + m->error("object.d is missing the ModuleInfo struct"); + fatal(); + } + // check for patch + else { + // The base struct should consist only of _flags/_index. + if (Module::moduleinfo->structsize != 4 + 4) { + m->error("Unexpected size of struct object.ModuleInfo; " + "druntime version does not match compiler (see -v)"); + fatal(); } - // check for patch - else - { - // The base struct should consist only of _flags/_index. - if (Module::moduleinfo->structsize != 4 + 4) - { - m->error("Unexpected size of struct object.ModuleInfo; " - "druntime version does not match compiler (see -v)"); - fatal(); - } + } + + // use the RTTIBuilder + RTTIBuilder b(Module::moduleinfo); + + // some types + llvm::Type *const moduleInfoPtrTy = DtoPtrToType(Module::moduleinfo->type); + LLType *classinfoTy = Type::typeinfoclass->type->ctype->getLLType(); + + // importedModules[] + std::vector importInits; + LLConstant *importedModules = 0; + llvm::ArrayType *importedModulesTy = 0; + for (size_t i = 0; i < m->aimports.dim; i++) { + Module *mod = static_cast(m->aimports.data[i]); + if (!mod->needModuleInfo() || mod == m) + continue; + + importInits.push_back( + DtoBitCast(getIrModule(mod)->moduleInfoSymbol(), moduleInfoPtrTy)); + } + // has import array? + if (!importInits.empty()) { + importedModulesTy = + llvm::ArrayType::get(moduleInfoPtrTy, importInits.size()); + importedModules = LLConstantArray::get(importedModulesTy, importInits); + } + + // localClasses[] + LLConstant *localClasses = 0; + llvm::ArrayType *localClassesTy = 0; + ClassDeclarations aclasses; + // printf("members->dim = %d\n", members->dim); + for (size_t i = 0; i < m->members->dim; i++) { + (*m->members)[i]->addLocalClass(&aclasses); + } + // fill inits + std::vector classInits; + for (size_t i = 0; i < aclasses.dim; i++) { + ClassDeclaration *cd = aclasses[i]; + DtoResolveClass(cd); + + if (cd->isInterfaceDeclaration()) { + IF_LOG Logger::println("skipping interface '%s' in moduleinfo", + cd->toPrettyChars()); + continue; + } else if (cd->sizeok != SIZEOKdone) { + IF_LOG Logger::println( + "skipping opaque class declaration '%s' in moduleinfo", + cd->toPrettyChars()); + continue; } + IF_LOG Logger::println("class: %s", cd->toPrettyChars()); + LLConstant *c = + DtoBitCast(getIrAggr(cd)->getClassInfoSymbol(), classinfoTy); + classInits.push_back(c); + } + // has class array? + if (!classInits.empty()) { + localClassesTy = llvm::ArrayType::get(classinfoTy, classInits.size()); + localClasses = LLConstantArray::get(localClassesTy, classInits); + } - // use the RTTIBuilder - RTTIBuilder b(Module::moduleinfo); +// These must match the values in druntime/src/object_.d +#define MIstandalone 4 +#define MItlsctor 8 +#define MItlsdtor 0x10 +#define MIctor 0x20 +#define MIdtor 0x40 +#define MIxgetMembers 0x80 +#define MIictor 0x100 +#define MIunitTest 0x200 +#define MIimportedModules 0x400 +#define MIlocalClasses 0x800 +#define MInew 0x80000000 // it's the "new" layout - // some types - llvm::Type* const moduleInfoPtrTy = DtoPtrToType(Module::moduleinfo->type); - LLType* classinfoTy = Type::typeinfoclass->type->ctype->getLLType(); + llvm::Function *fsharedctor = build_module_shared_ctor(m); + llvm::Function *fshareddtor = build_module_shared_dtor(m); + llvm::Function *funittest = build_module_unittest(m); + llvm::Function *fctor = build_module_ctor(m); + llvm::Function *fdtor = build_module_dtor(m); - // importedModules[] - std::vector importInits; - LLConstant* importedModules = 0; - llvm::ArrayType* importedModulesTy = 0; - for (size_t i = 0; i < m->aimports.dim; i++) - { - Module *mod = static_cast(m->aimports.data[i]); - if (!mod->needModuleInfo() || mod == m) continue; - - importInits.push_back( - DtoBitCast(getIrModule(mod)->moduleInfoSymbol(), moduleInfoPtrTy)); - } - // has import array? - if (!importInits.empty()) - { - importedModulesTy = llvm::ArrayType::get(moduleInfoPtrTy, importInits.size()); - importedModules = LLConstantArray::get(importedModulesTy, importInits); - } - - // localClasses[] - LLConstant* localClasses = 0; - llvm::ArrayType* localClassesTy = 0; - ClassDeclarations aclasses; - //printf("members->dim = %d\n", members->dim); - for (size_t i = 0; i < m->members->dim; i++) - { - (*m->members)[i]->addLocalClass(&aclasses); - } - // fill inits - std::vector classInits; - for (size_t i = 0; i < aclasses.dim; i++) - { - ClassDeclaration* cd = aclasses[i]; - DtoResolveClass(cd); - - if (cd->isInterfaceDeclaration()) - { - IF_LOG Logger::println("skipping interface '%s' in moduleinfo", cd->toPrettyChars()); - continue; - } - else if (cd->sizeok != SIZEOKdone) - { - IF_LOG Logger::println("skipping opaque class declaration '%s' in moduleinfo", cd->toPrettyChars()); - continue; - } - IF_LOG Logger::println("class: %s", cd->toPrettyChars()); - LLConstant *c = DtoBitCast(getIrAggr(cd)->getClassInfoSymbol(), classinfoTy); - classInits.push_back(c); - } - // has class array? - if (!classInits.empty()) - { - localClassesTy = llvm::ArrayType::get(classinfoTy, classInits.size()); - localClasses = LLConstantArray::get(localClassesTy, classInits); - } - - // These must match the values in druntime/src/object_.d - #define MIstandalone 4 - #define MItlsctor 8 - #define MItlsdtor 0x10 - #define MIctor 0x20 - #define MIdtor 0x40 - #define MIxgetMembers 0x80 - #define MIictor 0x100 - #define MIunitTest 0x200 - #define MIimportedModules 0x400 - #define MIlocalClasses 0x800 - #define MInew 0x80000000 // it's the "new" layout - - llvm::Function* fsharedctor = build_module_shared_ctor(m); - llvm::Function* fshareddtor = build_module_shared_dtor(m); - llvm::Function* funittest = build_module_unittest(m); - llvm::Function* fctor = build_module_ctor(m); - llvm::Function* fdtor = build_module_dtor(m); - - unsigned flags = MInew; - if (fctor) - flags |= MItlsctor; - if (fdtor) - flags |= MItlsdtor; - if (fsharedctor) - flags |= MIctor; - if (fshareddtor) - flags |= MIdtor; + unsigned flags = MInew; + if (fctor) + flags |= MItlsctor; + if (fdtor) + flags |= MItlsdtor; + if (fsharedctor) + flags |= MIctor; + if (fshareddtor) + flags |= MIdtor; #if 0 if (fgetmembers) flags |= MIxgetMembers; if (fictor) flags |= MIictor; #endif - if (funittest) - flags |= MIunitTest; - if (importedModules) - flags |= MIimportedModules; - if (localClasses) - flags |= MIlocalClasses; + if (funittest) + flags |= MIunitTest; + if (importedModules) + flags |= MIimportedModules; + if (localClasses) + flags |= MIlocalClasses; - if (!m->needmoduleinfo) - flags |= MIstandalone; + if (!m->needmoduleinfo) + flags |= MIstandalone; - b.push_uint(flags); // flags - b.push_uint(0); // index + b.push_uint(flags); // flags + b.push_uint(0); // index - if (fctor) - b.push(fctor); - if (fdtor) - b.push(fdtor); - if (fsharedctor) - b.push(fsharedctor); - if (fshareddtor) - b.push(fshareddtor); + if (fctor) + b.push(fctor); + if (fdtor) + b.push(fdtor); + if (fsharedctor) + b.push(fsharedctor); + if (fshareddtor) + b.push(fshareddtor); #if 0 if (fgetmembers) b.push(fgetmembers); if (fictor) b.push(fictor); #endif - if (funittest) - b.push(funittest); - if (importedModules) { - b.push_size(importInits.size()); - b.push(importedModules); - } - if (localClasses) { - b.push_size(classInits.size()); - b.push(localClasses); - } + if (funittest) + b.push(funittest); + if (importedModules) { + b.push_size(importInits.size()); + b.push(importedModules); + } + if (localClasses) { + b.push_size(classInits.size()); + b.push(localClasses); + } - // Put out module name as a 0-terminated string. - const char *name = m->toPrettyChars(); - const size_t len = strlen(name) + 1; - llvm::IntegerType *it = llvm::IntegerType::getInt8Ty(gIR->context()); - llvm::ArrayType *at = llvm::ArrayType::get(it, len); - b.push(toConstantArray(it, at, name, len, false)); + // Put out module name as a 0-terminated string. + const char *name = m->toPrettyChars(); + const size_t len = strlen(name) + 1; + llvm::IntegerType *it = llvm::IntegerType::getInt8Ty(gIR->context()); + llvm::ArrayType *at = llvm::ArrayType::get(it, len); + b.push(toConstantArray(it, at, name, len, false)); - // create and set initializer - LLGlobalVariable *moduleInfoSym = getIrModule(m)->moduleInfoSymbol(); - b.finalize(moduleInfoSym->getType()->getPointerElementType(), moduleInfoSym); - moduleInfoSym->setLinkage(llvm::GlobalValue::ExternalLinkage); + // create and set initializer + LLGlobalVariable *moduleInfoSym = getIrModule(m)->moduleInfoSymbol(); + b.finalize(moduleInfoSym->getType()->getPointerElementType(), moduleInfoSym); + moduleInfoSym->setLinkage(llvm::GlobalValue::ExternalLinkage); - if (global.params.isLinux) { - if (emitFullModuleInfo) - build_dso_registry_calls(mangle(m), moduleInfoSym); - else - build_module_ref(mangle(m), moduleInfoSym); - } else { - // build the modulereference and ctor for registering it - LLFunction* mictor = build_module_reference_and_ctor(mangle(m), moduleInfoSym); - AppendFunctionToLLVMGlobalCtorsDtors(mictor, 65535, true); - } + if (global.params.isLinux) { + if (emitFullModuleInfo) + build_dso_registry_calls(mangle(m), moduleInfoSym); + else + build_module_ref(mangle(m), moduleInfoSym); + } else { + // build the modulereference and ctor for registering it + LLFunction *mictor = + build_module_reference_and_ctor(mangle(m), moduleInfoSym); + AppendFunctionToLLVMGlobalCtorsDtors(mictor, 65535, true); + } } diff --git a/gen/naked.cpp b/gen/naked.cpp index e225219d47..ac0b672821 100644 --- a/gen/naked.cpp +++ b/gen/naked.cpp @@ -28,273 +28,260 @@ void AsmStatement_toNakedIR(AsmStatement *stmt, IRState *irs); ////////////////////////////////////////////////////////////////////////////////////////// class ToNakedIRVisitor : public Visitor { - IRState *irs; + IRState *irs; + public: + ToNakedIRVisitor(IRState *irs) : irs(irs) {} - ToNakedIRVisitor(IRState *irs) : irs(irs) { } + ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// + // Import all functions from class Visitor + using Visitor::visit; - // Import all functions from class Visitor - using Visitor::visit; + ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// + void visit(Statement *stmt) LLVM_OVERRIDE { + error(Loc(), "Statement not allowed in naked function"); + } - void visit(Statement *stmt) LLVM_OVERRIDE { - error(Loc(), "Statement not allowed in naked function"); + ////////////////////////////////////////////////////////////////////////// + + void visit(AsmStatement *stmt) LLVM_OVERRIDE { + AsmStatement_toNakedIR(stmt, irs); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(CompoundStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("CompoundStatement::toNakedIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; + + if (stmt->statements) + for (auto s : *stmt->statements) + if (s) + s->accept(this); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(ExpStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("ExpStatement::toNakedIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; + + // This happens only if there is a ; at the end: + // asm { naked; ... }; + // Is this a legal AST? + if (!stmt->exp) + return; + + // only expstmt supported in declarations + if (!stmt->exp || stmt->exp->op != TOKdeclaration) { + visit(static_cast(stmt)); + return; } - ////////////////////////////////////////////////////////////////////////// + DeclarationExp *d = static_cast(stmt->exp); + VarDeclaration *vd = d->declaration->isVarDeclaration(); + FuncDeclaration *fd = d->declaration->isFuncDeclaration(); + EnumDeclaration *ed = d->declaration->isEnumDeclaration(); - void visit(AsmStatement *stmt) LLVM_OVERRIDE { - AsmStatement_toNakedIR(stmt, irs); + // and only static variable/function declaration + // no locals or nested stuffies! + if (!vd && !fd && !ed) { + visit(static_cast(stmt)); + return; + } else if (vd && !(vd->storage_class & (STCstatic | STCmanifest))) { + error(vd->loc, "non-static variable '%s' not allowed in naked function", + vd->toChars()); + return; + } else if (fd && !fd->isStatic()) { + error(fd->loc, + "non-static nested function '%s' not allowed in naked function", + fd->toChars()); + return; } + // enum decls should always be safe - ////////////////////////////////////////////////////////////////////////// + // make sure the symbols gets processed + // TODO: codegen() here is likely incorrect + Declaration_codegen(d->declaration, irs); + } - void visit(CompoundStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("CompoundStatement::toNakedIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////// - if (stmt->statements) - for (auto s : *stmt->statements) - if (s) s->accept(this); - } + void visit(LabelStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("LabelStatement::toNakedIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; - ////////////////////////////////////////////////////////////////////////// + printLabelName(irs->nakedAsm, mangleExact(irs->func()->decl), + stmt->ident->toChars()); + irs->nakedAsm << ":"; - void visit(ExpStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("ExpStatement::toNakedIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // This happens only if there is a ; at the end: - // asm { naked; ... }; - // Is this a legal AST? - if (!stmt->exp) return; - - // only expstmt supported in declarations - if (!stmt->exp || stmt->exp->op != TOKdeclaration) - { - visit(static_cast(stmt)); - return; - } - - DeclarationExp* d = static_cast(stmt->exp); - VarDeclaration* vd = d->declaration->isVarDeclaration(); - FuncDeclaration* fd = d->declaration->isFuncDeclaration(); - EnumDeclaration* ed = d->declaration->isEnumDeclaration(); - - // and only static variable/function declaration - // no locals or nested stuffies! - if (!vd && !fd && !ed) - { - visit(static_cast(stmt)); - return; - } - else if (vd && !(vd->storage_class & (STCstatic | STCmanifest))) - { - error(vd->loc, "non-static variable '%s' not allowed in naked function", vd->toChars()); - return; - } - else if (fd && !fd->isStatic()) - { - error(fd->loc, "non-static nested function '%s' not allowed in naked function", fd->toChars()); - return; - } - // enum decls should always be safe - - // make sure the symbols gets processed - // TODO: codegen() here is likely incorrect - Declaration_codegen(d->declaration, irs); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(LabelStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("LabelStatement::toNakedIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - printLabelName(irs->nakedAsm, mangleExact(irs->func()->decl), stmt->ident->toChars()); - irs->nakedAsm << ":"; - - if (stmt->statement) - stmt->statement->accept(this); - } + if (stmt->statement) + stmt->statement->accept(this); + } }; ////////////////////////////////////////////////////////////////////////////////////////// -void DtoDefineNakedFunction(FuncDeclaration* fd) -{ - IF_LOG Logger::println("DtoDefineNakedFunction(%s)", mangleExact(fd)); - LOG_SCOPE; +void DtoDefineNakedFunction(FuncDeclaration *fd) { + IF_LOG Logger::println("DtoDefineNakedFunction(%s)", mangleExact(fd)); + LOG_SCOPE; - gIR->functions.push_back(getIrFunc(fd)); + gIR->functions.push_back(getIrFunc(fd)); - // we need to do special processing on the body, since we only want - // to allow actual inline asm blocks to reach the final asm output + // we need to do special processing on the body, since we only want + // to allow actual inline asm blocks to reach the final asm output - std::ostringstream& asmstr = gIR->nakedAsm; + std::ostringstream &asmstr = gIR->nakedAsm; - // build function header + // build function header - // FIXME: could we perhaps use llvm asmwriter to give us these details ? + // FIXME: could we perhaps use llvm asmwriter to give us these details ? - const char* mangle = mangleExact(fd); - std::ostringstream tmpstr; + const char *mangle = mangleExact(fd); + std::ostringstream tmpstr; - bool const isWin = global.params.targetTriple.isOSWindows(); - bool const isOSX = (global.params.targetTriple.getOS() == llvm::Triple::Darwin || - global.params.targetTriple.getOS() == llvm::Triple::MacOSX); + bool const isWin = global.params.targetTriple.isOSWindows(); + bool const isOSX = + (global.params.targetTriple.getOS() == llvm::Triple::Darwin || + global.params.targetTriple.getOS() == llvm::Triple::MacOSX); - // osx is different - // also mangling has an extra underscore prefixed - if (isOSX) - { - std::string section = "text"; - bool weak = false; - if (DtoIsTemplateInstance(fd)) - { - tmpstr << "section\t__TEXT,__textcoal_nt,coalesced,pure_instructions"; - section = tmpstr.str(); - weak = true; - } - asmstr << "\t." << section << std::endl; - asmstr << "\t.align\t4, 0x90" << std::endl; - asmstr << "\t.globl\t_" << mangle << std::endl; - if (weak) - { - asmstr << "\t.weak_definition\t_" << mangle << std::endl; - } - asmstr << "_" << mangle << ":" << std::endl; + // osx is different + // also mangling has an extra underscore prefixed + if (isOSX) { + std::string section = "text"; + bool weak = false; + if (DtoIsTemplateInstance(fd)) { + tmpstr << "section\t__TEXT,__textcoal_nt,coalesced,pure_instructions"; + section = tmpstr.str(); + weak = true; } - // Windows is different - else if (isWin) - { - std::string fullMangle; - if ( global.params.targetTriple.isWindowsGNUEnvironment() - && !global.params.targetTriple.isArch64Bit() ) - { - fullMangle = "_"; - } - fullMangle += mangle; - - asmstr << "\t.def\t" << fullMangle << ";" << std::endl; - // hard code these two numbers for now since gas ignores .scl and llvm - // is defaulting to .type 32 for everything I have seen - asmstr << "\t.scl 2;" << std::endl; - asmstr << "\t.type 32;" << std::endl; - asmstr << "\t.endef" << std::endl; - - if (DtoIsTemplateInstance(fd)) - { - asmstr << "\t.section\t.text$" << fullMangle << ",\"xr\"" << std::endl; - asmstr << "\t.linkonce\tdiscard" << std::endl; - } - else - asmstr << "\t.text" << std::endl; - asmstr << "\t.globl\t" << fullMangle << std::endl; - asmstr << "\t.align\t16, 0x90" << std::endl; - asmstr << fullMangle << ":" << std::endl; + asmstr << "\t." << section << std::endl; + asmstr << "\t.align\t4, 0x90" << std::endl; + asmstr << "\t.globl\t_" << mangle << std::endl; + if (weak) { + asmstr << "\t.weak_definition\t_" << mangle << std::endl; } - else - { - if (DtoIsTemplateInstance(fd)) - { - asmstr << "\t.section\t.text." << mangle << ",\"axG\",@progbits," - << mangle << ",comdat" << std::endl; - asmstr << "\t.weak\t" << mangle << std::endl; - } - else - { - asmstr << "\t.text" << std::endl; - asmstr << "\t.globl\t" << mangle << std::endl; - } - asmstr << "\t.align\t16, 0x90" << std::endl; - asmstr << "\t.type\t" << mangle << ",@function" << std::endl; - asmstr << mangle << ":" << std::endl; + asmstr << "_" << mangle << ":" << std::endl; + } + // Windows is different + else if (isWin) { + std::string fullMangle; + if (global.params.targetTriple.isWindowsGNUEnvironment() && + !global.params.targetTriple.isArch64Bit()) { + fullMangle = "_"; } + fullMangle += mangle; - // emit body - ToNakedIRVisitor v(gIR); - fd->fbody->accept(&v); + asmstr << "\t.def\t" << fullMangle << ";" << std::endl; + // hard code these two numbers for now since gas ignores .scl and llvm + // is defaulting to .type 32 for everything I have seen + asmstr << "\t.scl 2;" << std::endl; + asmstr << "\t.type 32;" << std::endl; + asmstr << "\t.endef" << std::endl; - // We could have generated new errors in toNakedIR(), but we are in codegen - // already so we have to abort here. - if (global.errors) - fatal(); - - // emit size after body - // llvm does this on linux, but not on osx or Win - if (!(isWin || isOSX)) - { - asmstr << "\t.size\t" << mangle << ", .-" << mangle << std::endl << std::endl; + if (DtoIsTemplateInstance(fd)) { + asmstr << "\t.section\t.text$" << fullMangle << ",\"xr\"" << std::endl; + asmstr << "\t.linkonce\tdiscard" << std::endl; + } else + asmstr << "\t.text" << std::endl; + asmstr << "\t.globl\t" << fullMangle << std::endl; + asmstr << "\t.align\t16, 0x90" << std::endl; + asmstr << fullMangle << ":" << std::endl; + } else { + if (DtoIsTemplateInstance(fd)) { + asmstr << "\t.section\t.text." << mangle << ",\"axG\",@progbits," + << mangle << ",comdat" << std::endl; + asmstr << "\t.weak\t" << mangle << std::endl; + } else { + asmstr << "\t.text" << std::endl; + asmstr << "\t.globl\t" << mangle << std::endl; } + asmstr << "\t.align\t16, 0x90" << std::endl; + asmstr << "\t.type\t" << mangle << ",@function" << std::endl; + asmstr << mangle << ":" << std::endl; + } - gIR->module.appendModuleInlineAsm(asmstr.str()); - asmstr.str(""); + // emit body + ToNakedIRVisitor v(gIR); + fd->fbody->accept(&v); - gIR->functions.pop_back(); + // We could have generated new errors in toNakedIR(), but we are in codegen + // already so we have to abort here. + if (global.errors) + fatal(); + + // emit size after body + // llvm does this on linux, but not on osx or Win + if (!(isWin || isOSX)) { + asmstr << "\t.size\t" << mangle << ", .-" << mangle << std::endl + << std::endl; + } + + gIR->module.appendModuleInlineAsm(asmstr.str()); + asmstr.str(""); + + gIR->functions.pop_back(); } ////////////////////////////////////////////////////////////////////////////////////////// -void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc& loc, FuncDeclaration* fdecl) -{ - IF_LOG Logger::println("emitABIReturnAsmStmt(%s)", mangleExact(fdecl)); - LOG_SCOPE; +void emitABIReturnAsmStmt(IRAsmBlock *asmblock, Loc &loc, + FuncDeclaration *fdecl) { + IF_LOG Logger::println("emitABIReturnAsmStmt(%s)", mangleExact(fdecl)); + LOG_SCOPE; - IRAsmStmt* as = new IRAsmStmt; + IRAsmStmt *as = new IRAsmStmt; - LLType* llretTy = DtoType(fdecl->type->nextOf()); - asmblock->retty = llretTy; - asmblock->retn = 1; + LLType *llretTy = DtoType(fdecl->type->nextOf()); + asmblock->retty = llretTy; + asmblock->retn = 1; - // FIXME: This should probably be handled by the TargetABI somehow. - // It should be able to do this for a greater variety of types. + // FIXME: This should probably be handled by the TargetABI somehow. + // It should be able to do this for a greater variety of types. - // x86 - if (global.params.targetTriple.getArch() == llvm::Triple::x86) - { - LINK l = fdecl->linkage; - assert((l == LINKd || l == LINKc || l == LINKwindows) && "invalid linkage for asm implicit return"); + // x86 + if (global.params.targetTriple.getArch() == llvm::Triple::x86) { + LINK l = fdecl->linkage; + assert((l == LINKd || l == LINKc || l == LINKwindows) && + "invalid linkage for asm implicit return"); - Type* rt = fdecl->type->nextOf()->toBasetype(); - if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || rt->ty == Taarray) - { - if (rt->size() == 8) { - as->out_c = "=A,"; - } else { - as->out_c = "={ax},"; - } + Type *rt = fdecl->type->nextOf()->toBasetype(); + if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || + rt->ty == Taarray) { + if (rt->size() == 8) { + as->out_c = "=A,"; + } else { + as->out_c = "={ax},"; + } + } else if (rt->isfloating()) { + if (rt->iscomplex()) { + if (fdecl->linkage == LINKd) { + // extern(D) always returns on the FPU stack + as->out_c = "={st},={st(1)},"; + asmblock->retn = 2; + } else if (rt->ty == Tcomplex32) { + // extern(C) cfloat is return as i64 + as->out_c = "=A,"; + asmblock->retty = LLType::getInt64Ty(gIR->context()); + } else { + // cdouble and creal extern(C) are returned in pointer + // don't add anything! + asmblock->retty = LLType::getVoidTy(gIR->context()); + asmblock->retn = 0; + return; } - else if (rt->isfloating()) - { - if (rt->iscomplex()) { - if (fdecl->linkage == LINKd) { - // extern(D) always returns on the FPU stack - as->out_c = "={st},={st(1)},"; - asmblock->retn = 2; - } else if (rt->ty == Tcomplex32) { - // extern(C) cfloat is return as i64 - as->out_c = "=A,"; - asmblock->retty = LLType::getInt64Ty(gIR->context()); - } else { - // cdouble and creal extern(C) are returned in pointer - // don't add anything! - asmblock->retty = LLType::getVoidTy(gIR->context()); - asmblock->retn = 0; - return; - } - } else { - as->out_c = "={st},"; - } - } - else if (rt->ty == Tarray || rt->ty == Tdelegate) - { - as->out_c = "={ax},={dx},"; - asmblock->retn = 2; - #if 0 + } else { + as->out_c = "={st},"; + } + } else if (rt->ty == Tarray || rt->ty == Tdelegate) { + as->out_c = "={ax},={dx},"; + asmblock->retn = 2; +#if 0 // this is to show how to allocate a temporary for the return value // in case the appropriate multi register constraint isn't supported. // this way abi return from inline asm can still be emulated. @@ -318,159 +305,149 @@ void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc& loc, FuncDeclaration* fdecl // done, we don't want anything pushed in the front of the block return; - #endif - } - else - { - error(loc, "unimplemented return type '%s' for implicit abi return", rt->toChars()); - fatal(); - } +#endif + } else { + error(loc, "unimplemented return type '%s' for implicit abi return", + rt->toChars()); + fatal(); } + } - // x86_64 - else if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) - { - LINK l = fdecl->linkage; - /* TODO: Check if this works with extern(Windows), completely untested. - * In particular, returning cdouble may not work with - * extern(Windows) since according to X86CallingConv.td it - * doesn't allow XMM1 to be used. - * (So is extern(C), but that should be fine as the calling convention - * is identical to that of extern(D)) - */ - assert((l == LINKd || l == LINKc || l == LINKwindows) && "invalid linkage for asm implicit return"); + // x86_64 + else if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) { + LINK l = fdecl->linkage; + /* TODO: Check if this works with extern(Windows), completely untested. + * In particular, returning cdouble may not work with + * extern(Windows) since according to X86CallingConv.td it + * doesn't allow XMM1 to be used. + * (So is extern(C), but that should be fine as the calling convention + * is identical to that of extern(D)) + */ + assert((l == LINKd || l == LINKc || l == LINKwindows) && + "invalid linkage for asm implicit return"); - Type* rt = fdecl->type->nextOf()->toBasetype(); - if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || rt->ty == Taarray) - { - as->out_c = "={ax},"; - } - else if (rt->isfloating()) - { - if (rt == Type::tcomplex80) { - // On x87 stack, re=st, im=st(1) - as->out_c = "={st},={st(1)},"; - asmblock->retn = 2; - } else if (rt == Type::tfloat80 || rt == Type::timaginary80) { - // On x87 stack - as->out_c = "={st},"; - } else if (l != LINKd && rt == Type::tcomplex32) { - // LLVM and GCC disagree on how to return {float, float}. - // For compatibility, use the GCC/LLVM-GCC way for extern(C/Windows) - // extern(C) cfloat -> %xmm0 (extract two floats) - as->out_c = "={xmm0},"; - asmblock->retty = LLType::getDoubleTy(gIR->context()); - } else if (rt->iscomplex()) { - // cdouble and extern(D) cfloat -> re=%xmm0, im=%xmm1 - as->out_c = "={xmm0},={xmm1},"; - asmblock->retn = 2; - } else { - // Plain float/double/ifloat/idouble - as->out_c = "={xmm0},"; - } - } - else if (rt->ty == Tarray || rt->ty == Tdelegate) - { - as->out_c = "={ax},={dx},"; - asmblock->retn = 2; - } - else - { - error(loc, "unimplemented return type '%s' for implicit abi return", rt->toChars()); - fatal(); - } + Type *rt = fdecl->type->nextOf()->toBasetype(); + if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || + rt->ty == Taarray) { + as->out_c = "={ax},"; + } else if (rt->isfloating()) { + if (rt == Type::tcomplex80) { + // On x87 stack, re=st, im=st(1) + as->out_c = "={st},={st(1)},"; + asmblock->retn = 2; + } else if (rt == Type::tfloat80 || rt == Type::timaginary80) { + // On x87 stack + as->out_c = "={st},"; + } else if (l != LINKd && rt == Type::tcomplex32) { + // LLVM and GCC disagree on how to return {float, float}. + // For compatibility, use the GCC/LLVM-GCC way for extern(C/Windows) + // extern(C) cfloat -> %xmm0 (extract two floats) + as->out_c = "={xmm0},"; + asmblock->retty = LLType::getDoubleTy(gIR->context()); + } else if (rt->iscomplex()) { + // cdouble and extern(D) cfloat -> re=%xmm0, im=%xmm1 + as->out_c = "={xmm0},={xmm1},"; + asmblock->retn = 2; + } else { + // Plain float/double/ifloat/idouble + as->out_c = "={xmm0},"; + } + } else if (rt->ty == Tarray || rt->ty == Tdelegate) { + as->out_c = "={ax},={dx},"; + asmblock->retn = 2; + } else { + error(loc, "unimplemented return type '%s' for implicit abi return", + rt->toChars()); + fatal(); } + } - // unsupported - else - { - error(loc, "this target (%s) does not implement inline asm falling off the end of the function", global.params.targetTriple.str().c_str()); - fatal(); - } + // unsupported + else { + error(loc, "this target (%s) does not implement inline asm falling off the " + "end of the function", + global.params.targetTriple.str().c_str()); + fatal(); + } - // return values always go in the front - asmblock->s.push_front(as); + // return values always go in the front + asmblock->s.push_front(as); } ////////////////////////////////////////////////////////////////////////////////////////// // sort of kinda related to naked ... -DValue * DtoInlineAsmExpr(Loc& loc, FuncDeclaration * fd, Expressions * arguments) -{ - IF_LOG Logger::println("DtoInlineAsmExpr @ %s", loc.toChars()); - LOG_SCOPE; +DValue *DtoInlineAsmExpr(Loc &loc, FuncDeclaration *fd, + Expressions *arguments) { + IF_LOG Logger::println("DtoInlineAsmExpr @ %s", loc.toChars()); + LOG_SCOPE; - TemplateInstance* ti = fd->toParent()->isTemplateInstance(); - assert(ti && "invalid inline __asm expr"); + TemplateInstance *ti = fd->toParent()->isTemplateInstance(); + assert(ti && "invalid inline __asm expr"); - assert(arguments->dim >= 2 && "invalid __asm call"); + assert(arguments->dim >= 2 && "invalid __asm call"); - // get code param - Expression* e = (*arguments)[0]; - IF_LOG Logger::println("code exp: %s", e->toChars()); - StringExp* se = static_cast(e); - if (e->op != TOKstring || se->sz != 1) - { - e->error("__asm code argument is not a char[] string literal"); - fatal(); - } - std::string code(static_cast(se->string), se->len); + // get code param + Expression *e = (*arguments)[0]; + IF_LOG Logger::println("code exp: %s", e->toChars()); + StringExp *se = static_cast(e); + if (e->op != TOKstring || se->sz != 1) { + e->error("__asm code argument is not a char[] string literal"); + fatal(); + } + std::string code(static_cast(se->string), se->len); - // get constraints param - e = (*arguments)[1]; - IF_LOG Logger::println("constraint exp: %s", e->toChars()); - se = static_cast(e); - if (e->op != TOKstring || se->sz != 1) - { - e->error("__asm constraints argument is not a char[] string literal"); - fatal(); - } - std::string constraints(static_cast(se->string), se->len); + // get constraints param + e = (*arguments)[1]; + IF_LOG Logger::println("constraint exp: %s", e->toChars()); + se = static_cast(e); + if (e->op != TOKstring || se->sz != 1) { + e->error("__asm constraints argument is not a char[] string literal"); + fatal(); + } + std::string constraints(static_cast(se->string), se->len); - // build runtime arguments - size_t n = arguments->dim; + // build runtime arguments + size_t n = arguments->dim; - LLSmallVector args; - args.reserve(n-2); - std::vector argtypes; - argtypes.reserve(n-2); + LLSmallVector args; + args.reserve(n - 2); + std::vector argtypes; + argtypes.reserve(n - 2); - for (size_t i = 2; i < n; i++) - { - args.push_back(toElem((*arguments)[i])->getRVal()); - argtypes.push_back(args.back()->getType()); + for (size_t i = 2; i < n; i++) { + args.push_back(toElem((*arguments)[i])->getRVal()); + argtypes.push_back(args.back()->getType()); + } + + // build asm function type + Type *type = fd->type->nextOf()->toBasetype(); + LLType *ret_type = DtoType(type); + llvm::FunctionType *FT = llvm::FunctionType::get(ret_type, argtypes, false); + + // build asm call + bool sideeffect = true; + llvm::InlineAsm *ia = llvm::InlineAsm::get(FT, code, constraints, sideeffect); + + llvm::Value *rv = gIR->ir->CreateCall(ia, args, ""); + + // work around missing tuple support for users of the return value + if (type->ty == Tstruct) { + // make a copy + llvm::Value *mem = DtoAlloca(type, ".__asm_tuple_ret"); + + TypeStruct *ts = static_cast(type); + size_t n = ts->sym->fields.dim; + for (size_t i = 0; i < n; i++) { + llvm::Value *v = gIR->ir->CreateExtractValue(rv, i, ""); + llvm::Value *gep = DtoGEPi(mem, 0, i); + DtoStore(v, gep); } - // build asm function type - Type* type = fd->type->nextOf()->toBasetype(); - LLType* ret_type = DtoType(type); - llvm::FunctionType* FT = llvm::FunctionType::get(ret_type, argtypes, false); + return new DVarValue(fd->type->nextOf(), mem); + } - // build asm call - bool sideeffect = true; - llvm::InlineAsm* ia = llvm::InlineAsm::get(FT, code, constraints, sideeffect); - - llvm::Value* rv = gIR->ir->CreateCall(ia, args, ""); - - // work around missing tuple support for users of the return value - if (type->ty == Tstruct) - { - // make a copy - llvm::Value* mem = DtoAlloca(type, ".__asm_tuple_ret"); - - TypeStruct* ts = static_cast(type); - size_t n = ts->sym->fields.dim; - for (size_t i = 0; i < n; i++) - { - llvm::Value* v = gIR->ir->CreateExtractValue(rv, i, ""); - llvm::Value* gep = DtoGEPi(mem, 0, i); - DtoStore(v, gep); - } - - return new DVarValue(fd->type->nextOf(), mem); - } - - // return call as im value - return new DImValue(fd->type->nextOf(), rv); + // return call as im value + return new DImValue(fd->type->nextOf(), rv); } diff --git a/gen/nested.cpp b/gen/nested.cpp index d81a31a902..0254fac847 100644 --- a/gen/nested.cpp +++ b/gen/nested.cpp @@ -24,529 +24,513 @@ // NESTED VARIABLE HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ -static void storeVariable(VarDeclaration *vd, LLValue *dst) -{ - LLValue *value = getIrLocal(vd)->value; - int ty = vd->type->ty; - FuncDeclaration *fd = getParentFunc(vd, true); - assert(fd && "No parent function for nested variable?"); - if (fd->needsClosure() && !vd->isRef() && (ty == Tstruct || ty == Tsarray) && isaPointer(value->getType())) { - // Copy structs and static arrays - LLValue *mem = DtoGcMalloc(vd->loc, DtoType(vd->type), ".gc_mem"); - DtoAggrCopy(mem, value); - DtoAlignedStore(mem, dst); - } else +static void storeVariable(VarDeclaration *vd, LLValue *dst) { + LLValue *value = getIrLocal(vd)->value; + int ty = vd->type->ty; + FuncDeclaration *fd = getParentFunc(vd, true); + assert(fd && "No parent function for nested variable?"); + if (fd->needsClosure() && !vd->isRef() && (ty == Tstruct || ty == Tsarray) && + isaPointer(value->getType())) { + // Copy structs and static arrays + LLValue *mem = DtoGcMalloc(vd->loc, DtoType(vd->type), ".gc_mem"); + DtoAggrCopy(mem, value); + DtoAlignedStore(mem, dst); + } else // Store the address into the frame DtoAlignedStore(value, dst); } -static unsigned getVthisIdx(AggregateDeclaration* ad) -{ - return getFieldGEPIndex(ad, ad->vthis); +static unsigned getVthisIdx(AggregateDeclaration *ad) { + return getFieldGEPIndex(ad, ad->vthis); } -static void DtoCreateNestedContextType(FuncDeclaration* fd); +static void DtoCreateNestedContextType(FuncDeclaration *fd); -DValue* DtoNestedVariable(Loc& loc, Type* astype, VarDeclaration* vd, bool byref) -{ - IF_LOG Logger::println("DtoNestedVariable for %s @ %s", vd->toChars(), loc.toChars()); - LOG_SCOPE; +DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd, + bool byref) { + IF_LOG Logger::println("DtoNestedVariable for %s @ %s", vd->toChars(), + loc.toChars()); + LOG_SCOPE; - //////////////////////////////////// - // Locate context value + //////////////////////////////////// + // Locate context value - Dsymbol* vdparent = vd->toParent2(); - assert(vdparent); + Dsymbol *vdparent = vd->toParent2(); + assert(vdparent); - IrFunction* irfunc = gIR->func(); + IrFunction *irfunc = gIR->func(); - // Check whether we can access the needed frame - FuncDeclaration *fd = irfunc->decl; - while (fd != vdparent) { - if (fd->isStatic()) { - error(loc, "function %s cannot access frame of function %s", irfunc->decl->toPrettyChars(), vdparent->toPrettyChars()); - return new DVarValue(astype, vd, llvm::UndefValue::get(DtoPtrToType(astype))); - } - fd = getParentFunc(fd, false); - assert(fd); + // Check whether we can access the needed frame + FuncDeclaration *fd = irfunc->decl; + while (fd != vdparent) { + if (fd->isStatic()) { + error(loc, "function %s cannot access frame of function %s", + irfunc->decl->toPrettyChars(), vdparent->toPrettyChars()); + return new DVarValue(astype, vd, + llvm::UndefValue::get(DtoPtrToType(astype))); } + fd = getParentFunc(fd, false); + assert(fd); + } - // is the nested variable in this scope? - if (vdparent == irfunc->decl) - { - LLValue* val = getIrValue(vd); - return new DVarValue(astype, vd, val); - } - - LLValue *dwarfValue = 0; -#if LDC_LLVM_VER >= 306 - std::vector dwarfAddr; -#else - std::vector dwarfAddr; -#endif - - // get the nested context - LLValue* ctx = 0; - if (irfunc->nestedVar) { - // If this function has its own nested context struct, always load it. - ctx = irfunc->nestedVar; - dwarfValue = ctx; - } else if (irfunc->decl->isMember2()) { - // If this is a member function of a nested class without its own - // context, load the vthis member. - AggregateDeclaration* cd = irfunc->decl->isMember2(); - LLValue* val = irfunc->thisArg; - if (cd->isClassDeclaration()) - val = DtoLoad(val); - ctx = DtoLoad(DtoGEPi(val, 0, getVthisIdx(cd), ".vthis")); - } else { - // Otherwise, this is a simple nested function, load from the context - // argument. - ctx = DtoLoad(irfunc->nestArg); - dwarfValue = irfunc->nestArg; - if (global.params.symdebug) - gIR->DBuilder.OpDeref(dwarfAddr); - } - assert(ctx); - - DtoCreateNestedContextType(vdparent->isFuncDeclaration()); - assert(isIrLocalCreated(vd)); - - //////////////////////////////////// - // Extract variable from nested context - - LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(irfunc->frameType)); - IF_LOG { - Logger::cout() << "Context: " << *val << '\n'; - Logger::cout() << "of type: " << *irfunc->frameType << '\n'; - } - - unsigned vardepth = getIrLocal(vd)->nestedDepth; - unsigned funcdepth = irfunc->depth; - - IF_LOG { - Logger::cout() << "Variable: " << vd->toChars() << '\n'; - Logger::cout() << "Variable depth: " << vardepth << '\n'; - Logger::cout() << "Function: " << irfunc->decl->toChars() << '\n'; - Logger::cout() << "Function depth: " << funcdepth << '\n'; - } - - if (vardepth == funcdepth) { - // This is not always handled above because functions without - // variables accessed by nested functions don't create new frames. - IF_LOG Logger::println("Same depth"); - } else { - // Load frame pointer and index that... - if (dwarfValue && global.params.symdebug) { - gIR->DBuilder.OpOffset(dwarfAddr, val, vardepth); - gIR->DBuilder.OpDeref(dwarfAddr); - } - IF_LOG Logger::println("Lower depth"); - val = DtoGEPi(val, 0, vardepth); - IF_LOG Logger::cout() << "Frame index: " << *val << '\n'; - val = DtoAlignedLoad(val, (std::string(".frame.") + vdparent->toChars()).c_str()); - IF_LOG Logger::cout() << "Frame: " << *val << '\n'; - } - - int idx = getIrLocal(vd)->nestedIndex; - assert(idx != -1 && "Nested context not yet resolved for variable."); - - if (dwarfValue && global.params.symdebug) - gIR->DBuilder.OpOffset(dwarfAddr, val, idx); - - val = DtoGEPi(val, 0, idx, vd->toChars()); - IF_LOG { - Logger::cout() << "Addr: " << *val << '\n'; - Logger::cout() << "of type: " << *val->getType() << '\n'; - } - if (byref || (vd->isParameter() && getIrParameter(vd)->arg && getIrParameter(vd)->arg->byref)) { - val = DtoAlignedLoad(val); - //dwarfOpDeref(dwarfAddr); - IF_LOG { - Logger::cout() << "Was byref, now: " << *val << '\n'; - Logger::cout() << "of type: " << *val->getType() << '\n'; - } - } - - if (dwarfValue && global.params.symdebug) - gIR->DBuilder.EmitLocalVariable(dwarfValue, vd, 0, false, dwarfAddr); - + // is the nested variable in this scope? + if (vdparent == irfunc->decl) { + LLValue *val = getIrValue(vd); return new DVarValue(astype, vd, val); + } + + LLValue *dwarfValue = 0; +#if LDC_LLVM_VER >= 306 + std::vector dwarfAddr; +#else + std::vector dwarfAddr; +#endif + + // get the nested context + LLValue *ctx = 0; + if (irfunc->nestedVar) { + // If this function has its own nested context struct, always load it. + ctx = irfunc->nestedVar; + dwarfValue = ctx; + } else if (irfunc->decl->isMember2()) { + // If this is a member function of a nested class without its own + // context, load the vthis member. + AggregateDeclaration *cd = irfunc->decl->isMember2(); + LLValue *val = irfunc->thisArg; + if (cd->isClassDeclaration()) + val = DtoLoad(val); + ctx = DtoLoad(DtoGEPi(val, 0, getVthisIdx(cd), ".vthis")); + } else { + // Otherwise, this is a simple nested function, load from the context + // argument. + ctx = DtoLoad(irfunc->nestArg); + dwarfValue = irfunc->nestArg; + if (global.params.symdebug) + gIR->DBuilder.OpDeref(dwarfAddr); + } + assert(ctx); + + DtoCreateNestedContextType(vdparent->isFuncDeclaration()); + assert(isIrLocalCreated(vd)); + + //////////////////////////////////// + // Extract variable from nested context + + LLValue *val = DtoBitCast(ctx, LLPointerType::getUnqual(irfunc->frameType)); + IF_LOG { + Logger::cout() << "Context: " << *val << '\n'; + Logger::cout() << "of type: " << *irfunc->frameType << '\n'; + } + + unsigned vardepth = getIrLocal(vd)->nestedDepth; + unsigned funcdepth = irfunc->depth; + + IF_LOG { + Logger::cout() << "Variable: " << vd->toChars() << '\n'; + Logger::cout() << "Variable depth: " << vardepth << '\n'; + Logger::cout() << "Function: " << irfunc->decl->toChars() << '\n'; + Logger::cout() << "Function depth: " << funcdepth << '\n'; + } + + if (vardepth == funcdepth) { + // This is not always handled above because functions without + // variables accessed by nested functions don't create new frames. + IF_LOG Logger::println("Same depth"); + } else { + // Load frame pointer and index that... + if (dwarfValue && global.params.symdebug) { + gIR->DBuilder.OpOffset(dwarfAddr, val, vardepth); + gIR->DBuilder.OpDeref(dwarfAddr); + } + IF_LOG Logger::println("Lower depth"); + val = DtoGEPi(val, 0, vardepth); + IF_LOG Logger::cout() << "Frame index: " << *val << '\n'; + val = DtoAlignedLoad( + val, (std::string(".frame.") + vdparent->toChars()).c_str()); + IF_LOG Logger::cout() << "Frame: " << *val << '\n'; + } + + int idx = getIrLocal(vd)->nestedIndex; + assert(idx != -1 && "Nested context not yet resolved for variable."); + + if (dwarfValue && global.params.symdebug) + gIR->DBuilder.OpOffset(dwarfAddr, val, idx); + + val = DtoGEPi(val, 0, idx, vd->toChars()); + IF_LOG { + Logger::cout() << "Addr: " << *val << '\n'; + Logger::cout() << "of type: " << *val->getType() << '\n'; + } + if (byref || (vd->isParameter() && getIrParameter(vd)->arg && + getIrParameter(vd)->arg->byref)) { + val = DtoAlignedLoad(val); + // dwarfOpDeref(dwarfAddr); + IF_LOG { + Logger::cout() << "Was byref, now: " << *val << '\n'; + Logger::cout() << "of type: " << *val->getType() << '\n'; + } + } + + if (dwarfValue && global.params.symdebug) + gIR->DBuilder.EmitLocalVariable(dwarfValue, vd, 0, false, dwarfAddr); + + return new DVarValue(astype, vd, val); } -void DtoResolveNestedContext(Loc& loc, AggregateDeclaration *decl, LLValue *value) -{ - IF_LOG Logger::println("Resolving nested context"); - LOG_SCOPE; +void DtoResolveNestedContext(Loc &loc, AggregateDeclaration *decl, + LLValue *value) { + IF_LOG Logger::println("Resolving nested context"); + LOG_SCOPE; - // get context - LLValue* nest = DtoNestedContext(loc, decl); + // get context + LLValue *nest = DtoNestedContext(loc, decl); - // store into right location - if (!llvm::dyn_cast(nest)) { - // Need to make sure the declaration has already been resolved, because - // when multiple source files are specified on the command line, the - // frontend sometimes adds "nested" (i.e. a template in module B - // instantiated from module A with a type from module A instantiates - // another template from module B) into the wrong module, messing up - // our codegen order. - DtoResolveDsymbol(decl); + // store into right location + if (!llvm::dyn_cast(nest)) { + // Need to make sure the declaration has already been resolved, because + // when multiple source files are specified on the command line, the + // frontend sometimes adds "nested" (i.e. a template in module B + // instantiated from module A with a type from module A instantiates + // another template from module B) into the wrong module, messing up + // our codegen order. + DtoResolveDsymbol(decl); - unsigned idx = getVthisIdx(decl); - LLValue* gep = DtoGEPi(value,0,idx,".vthis"); - DtoStore(DtoBitCast(nest, gep->getType()->getContainedType(0)), gep); - } + unsigned idx = getVthisIdx(decl); + LLValue *gep = DtoGEPi(value, 0, idx, ".vthis"); + DtoStore(DtoBitCast(nest, gep->getType()->getContainedType(0)), gep); + } } -LLValue* DtoNestedContext(Loc& loc, Dsymbol* sym) -{ - IF_LOG Logger::println("DtoNestedContext for %s", sym->toPrettyChars()); - LOG_SCOPE; +LLValue *DtoNestedContext(Loc &loc, Dsymbol *sym) { + IF_LOG Logger::println("DtoNestedContext for %s", sym->toPrettyChars()); + LOG_SCOPE; - // Exit quickly for functions that accept a context pointer for ABI purposes, - // but do not actually read from it. - // - // We cannot simply fall back to retuning undef once we discover that we - // don't actually have a context to pass, because we sadly also need to - // catch invalid code here in the glue layer (see error() below). - if (FuncDeclaration* symfd = sym->isFuncDeclaration()) - { - // Make sure we've had a chance to analyze nested context usage - DtoCreateNestedContextType(symfd); + // Exit quickly for functions that accept a context pointer for ABI purposes, + // but do not actually read from it. + // + // We cannot simply fall back to retuning undef once we discover that we + // don't actually have a context to pass, because we sadly also need to + // catch invalid code here in the glue layer (see error() below). + if (FuncDeclaration *symfd = sym->isFuncDeclaration()) { + // Make sure we've had a chance to analyze nested context usage + DtoCreateNestedContextType(symfd); - int depth = getIrFunc(symfd)->depth; - Logger::println("for function of depth %d", depth); - if (depth == -1 || (depth == 0 && !symfd->closureVars.empty())) - { - Logger::println("function does not have context or creates its own " - "from scratch, returning undef"); - return llvm::UndefValue::get(getVoidPtrType()); - } + int depth = getIrFunc(symfd)->depth; + Logger::println("for function of depth %d", depth); + if (depth == -1 || (depth == 0 && !symfd->closureVars.empty())) { + Logger::println("function does not have context or creates its own " + "from scratch, returning undef"); + return llvm::UndefValue::get(getVoidPtrType()); + } + } + + // The function we are currently in, and the constructed object/called + // function might inherit a context pointer from. + IrFunction *irfunc = gIR->func(); + + bool fromParent = true; + + LLValue *val; + if (irfunc->nestedVar) { + // if this func has its own vars that are accessed by nested funcs + // use its own context + val = irfunc->nestedVar; + fromParent = false; + } else if (irfunc->nestArg) { + // otherwise, it may have gotten a context from the caller + val = DtoLoad(irfunc->nestArg); + } else if (irfunc->thisArg) { + // or just have a this argument + AggregateDeclaration *ad = irfunc->decl->isMember2(); + val = ad->isClassDeclaration() ? DtoLoad(irfunc->thisArg) : irfunc->thisArg; + if (!ad->vthis) { + // This is just a plain 'outer' reference of a class nested in a + // function (but without any variables in the nested context). + return val; + } + val = DtoLoad(DtoGEPi(val, 0, getVthisIdx(ad), ".vthis")); + } else { + if (sym->isFuncDeclaration()) { + // If we are here, the function actually needs its nested context + // and we cannot provide one. Thus, it's invalid code that is + // unfortunately not caught in the frontend (e.g. a function literal + // tries to call a nested function from the parent scope). + error(loc, + "function %s is a nested function and cannot be accessed from %s", + sym->toPrettyChars(), irfunc->decl->toPrettyChars()); + fatal(); } - // The function we are currently in, and the constructed object/called - // function might inherit a context pointer from. - IrFunction* irfunc = gIR->func(); + // Use null instead of e.g. LLVM's undef to not break bitwise + // comparison for instances of nested struct types which don't have any + // nested references. + return llvm::ConstantPointerNull::get(getVoidPtrType()); + } - bool fromParent = true; + FuncDeclaration *frameToPass = 0; + if (AggregateDeclaration *ad = sym->isAggregateDeclaration()) { + // If sym is a nested struct or a nested class, pass the frame + // of the function where sym is declared. + frameToPass = ad->toParent()->isFuncDeclaration(); + } else if (FuncDeclaration *symfd = sym->isFuncDeclaration()) { + // If sym is a nested function, and its parent context is different + // than the one we got, adjust it. + frameToPass = getParentFunc(symfd, true); + } - LLValue* val; - if (irfunc->nestedVar) - { - // if this func has its own vars that are accessed by nested funcs - // use its own context - val = irfunc->nestedVar; - fromParent = false; + if (frameToPass) { + IF_LOG Logger::println("Parent frame is from %s", frameToPass->toChars()); + FuncDeclaration *ctxfd = irfunc->decl; + IF_LOG Logger::println("Current function is %s", ctxfd->toChars()); + if (fromParent) { + ctxfd = getParentFunc(ctxfd, true); + assert(ctxfd && "Context from outer function, but no outer function?"); } - else if (irfunc->nestArg) - { - // otherwise, it may have gotten a context from the caller - val = DtoLoad(irfunc->nestArg); - } - else if (irfunc->thisArg) - { - // or just have a this argument - AggregateDeclaration* ad = irfunc->decl->isMember2(); - val = ad->isClassDeclaration() ? DtoLoad(irfunc->thisArg) : irfunc->thisArg; - if (!ad->vthis) - { - // This is just a plain 'outer' reference of a class nested in a - // function (but without any variables in the nested context). - return val; - } - val = DtoLoad(DtoGEPi(val, 0, getVthisIdx(ad), ".vthis")); - } - else - { - if (sym->isFuncDeclaration()) - { - // If we are here, the function actually needs its nested context - // and we cannot provide one. Thus, it's invalid code that is - // unfortunately not caught in the frontend (e.g. a function literal - // tries to call a nested function from the parent scope). - error(loc, "function %s is a nested function and cannot be accessed from %s", - sym->toPrettyChars(), irfunc->decl->toPrettyChars()); - fatal(); - } + IF_LOG Logger::println("Context is from %s", ctxfd->toChars()); - // Use null instead of e.g. LLVM's undef to not break bitwise - // comparison for instances of nested struct types which don't have any - // nested references. - return llvm::ConstantPointerNull::get(getVoidPtrType()); - } - - FuncDeclaration* frameToPass = 0; - if (AggregateDeclaration *ad = sym->isAggregateDeclaration()) { - // If sym is a nested struct or a nested class, pass the frame - // of the function where sym is declared. - frameToPass = ad->toParent()->isFuncDeclaration(); - } else if (FuncDeclaration* symfd = sym->isFuncDeclaration()) { - // If sym is a nested function, and its parent context is different - // than the one we got, adjust it. - frameToPass = getParentFunc(symfd, true); - } - - if (frameToPass) { - IF_LOG Logger::println("Parent frame is from %s", frameToPass->toChars()); - FuncDeclaration* ctxfd = irfunc->decl; - IF_LOG Logger::println("Current function is %s", ctxfd->toChars()); - if (fromParent) { - ctxfd = getParentFunc(ctxfd, true); - assert(ctxfd && "Context from outer function, but no outer function?"); - } - IF_LOG Logger::println("Context is from %s", ctxfd->toChars()); - - unsigned neededDepth = getIrFunc(frameToPass)->depth; - unsigned ctxDepth = getIrFunc(ctxfd)->depth; - - IF_LOG { - Logger::cout() << "Needed depth: " << neededDepth << '\n'; - Logger::cout() << "Context depth: " << ctxDepth << '\n'; - } - - if (neededDepth >= ctxDepth) { - // assert(neededDepth <= ctxDepth + 1 && "How are we going more than one nesting level up?"); - // fd needs the same context as we do, so all is well - IF_LOG Logger::println("Calling sibling function or directly nested function"); - } else { - val = DtoBitCast(val, LLPointerType::getUnqual(getIrFunc(ctxfd)->frameType)); - val = DtoGEPi(val, 0, neededDepth); - val = DtoAlignedLoad(val, - (std::string(".frame.") + frameToPass->toChars()).c_str()); - } - } + unsigned neededDepth = getIrFunc(frameToPass)->depth; + unsigned ctxDepth = getIrFunc(ctxfd)->depth; IF_LOG { - Logger::cout() << "result = " << *val << '\n'; - Logger::cout() << "of type " << *val->getType() << '\n'; + Logger::cout() << "Needed depth: " << neededDepth << '\n'; + Logger::cout() << "Context depth: " << ctxDepth << '\n'; } - return val; + + if (neededDepth >= ctxDepth) { + // assert(neededDepth <= ctxDepth + 1 && "How are we going more than one + // nesting level up?"); + // fd needs the same context as we do, so all is well + IF_LOG Logger::println( + "Calling sibling function or directly nested function"); + } else { + val = DtoBitCast(val, + LLPointerType::getUnqual(getIrFunc(ctxfd)->frameType)); + val = DtoGEPi(val, 0, neededDepth); + val = DtoAlignedLoad( + val, (std::string(".frame.") + frameToPass->toChars()).c_str()); + } + } + + IF_LOG { + Logger::cout() << "result = " << *val << '\n'; + Logger::cout() << "of type " << *val->getType() << '\n'; + } + return val; } -static void DtoCreateNestedContextType(FuncDeclaration* fd) -{ - IF_LOG Logger::println("DtoCreateNestedContextType for %s", fd->toPrettyChars()); - LOG_SCOPE +static void DtoCreateNestedContextType(FuncDeclaration *fd) { + IF_LOG Logger::println("DtoCreateNestedContextType for %s", + fd->toPrettyChars()); + LOG_SCOPE - DtoDeclareFunction(fd); + DtoDeclareFunction(fd); - IrFunction& irFunc = *getIrFunc(fd); + IrFunction &irFunc = *getIrFunc(fd); - if (irFunc.nestedContextCreated) - return; - irFunc.nestedContextCreated = true; + if (irFunc.nestedContextCreated) + return; + irFunc.nestedContextCreated = true; - FuncDeclaration* parentFunc = getParentFunc(fd, true); - // Make sure the parent has already been analyzed. - if (parentFunc) - DtoCreateNestedContextType(parentFunc); + FuncDeclaration *parentFunc = getParentFunc(fd, true); + // Make sure the parent has already been analyzed. + if (parentFunc) + DtoCreateNestedContextType(parentFunc); - // construct nested variables array - if (fd->closureVars.dim > 0) - { - Logger::println("has nested frame"); - // start with adding all enclosing parent frames until a static parent is reached + // construct nested variables array + if (fd->closureVars.dim > 0) { + Logger::println("has nested frame"); + // start with adding all enclosing parent frames until a static parent is + // reached - LLStructType* innerFrameType = NULL; - unsigned depth = 0; + LLStructType *innerFrameType = NULL; + unsigned depth = 0; - if (parentFunc) - { - IrFunction& parentIrFunc = *getIrFunc(parentFunc); - innerFrameType = parentIrFunc.frameType; - if (innerFrameType) - depth = parentIrFunc.depth + 1; - } - - irFunc.depth = depth; - - IF_LOG Logger::cout() << "Function " << fd->toChars() << " has depth " << depth << '\n'; - - AggrTypeBuilder builder(false); - - if (depth != 0) - { - assert(innerFrameType); - unsigned ptrSize = gDataLayout->getPointerSize(); - // Add frame pointer types for all but last frame - for (unsigned i = 0; i < (depth - 1); ++i) - builder.addType(innerFrameType->getElementType(i), ptrSize); - // Add frame pointer type for last frame - builder.addType(LLPointerType::getUnqual(innerFrameType), ptrSize); - } - - // Add the direct nested variables of this function, and update their indices to match. - // TODO: optimize ordering for minimal space usage? - for (auto vd : fd->closureVars) - { - unsigned alignment = DtoAlignment(vd); - if (alignment > 1) - builder.alignCurrentOffset(alignment); - - IrLocal& irLocal = *getIrLocal(vd, true); - irLocal.nestedIndex = builder.currentFieldIndex(); - irLocal.nestedDepth = depth; - - LLType* t = NULL; - if (vd->isParameter() && getIrParameter(vd)->arg) - { - // Parameters that are part of the LLVM signature will have - // storage associated with them (to handle byref etc.), so - // handle those cases specially by storing a pointer instead - // of a value. - const IrParameter* irparam = getIrParameter(vd); - const bool refout = vd->storage_class & (STCref | STCout); - const bool lazy = vd->storage_class & STClazy; - const bool byref = irparam->arg->byref; - const bool isVthisPtr = irparam->isVthis && !byref; - if (!(refout || (byref && !lazy)) || isVthisPtr) - { - // This will be copied to the nesting frame. - if (lazy) - t = irparam->value->getType()->getContainedType(0); - else - t = DtoMemType(vd->type); - } - else - t = irparam->value->getType(); - } - else if (isSpecialRefVar(vd)) - t = DtoType(vd->type->pointerTo()); - else - t = DtoMemType(vd->type); - - builder.addType(t, getTypeAllocSize(t)); - - IF_LOG Logger::cout() << "Nested var '" << vd->toChars() - << "' of type " << *t << "\n"; - } - - LLStructType* frameType = LLStructType::create(gIR->context(), builder.defaultTypes(), - std::string("nest.") + fd->toChars()); - - IF_LOG Logger::cout() << "frameType = " << *frameType << '\n'; - - // Store type in IrFunction - irFunc.frameType = frameType; - irFunc.frameTypeAlignment = builder.overallAlignment(); + if (parentFunc) { + IrFunction &parentIrFunc = *getIrFunc(parentFunc); + innerFrameType = parentIrFunc.frameType; + if (innerFrameType) + depth = parentIrFunc.depth + 1; } - else // no captured variables - { - if (parentFunc) - { - // Propagate context arg properties if the context arg is passed on unmodified. - IrFunction& parentIrFunc = *getIrFunc(parentFunc); - irFunc.frameType = parentIrFunc.frameType; - irFunc.frameTypeAlignment = parentIrFunc.frameTypeAlignment; - irFunc.depth = parentIrFunc.depth; - } + + irFunc.depth = depth; + + IF_LOG Logger::cout() << "Function " << fd->toChars() << " has depth " + << depth << '\n'; + + AggrTypeBuilder builder(false); + + if (depth != 0) { + assert(innerFrameType); + unsigned ptrSize = gDataLayout->getPointerSize(); + // Add frame pointer types for all but last frame + for (unsigned i = 0; i < (depth - 1); ++i) + builder.addType(innerFrameType->getElementType(i), ptrSize); + // Add frame pointer type for last frame + builder.addType(LLPointerType::getUnqual(innerFrameType), ptrSize); } + + // Add the direct nested variables of this function, and update their + // indices to match. + // TODO: optimize ordering for minimal space usage? + for (auto vd : fd->closureVars) { + unsigned alignment = DtoAlignment(vd); + if (alignment > 1) + builder.alignCurrentOffset(alignment); + + IrLocal &irLocal = *getIrLocal(vd, true); + irLocal.nestedIndex = builder.currentFieldIndex(); + irLocal.nestedDepth = depth; + + LLType *t = NULL; + if (vd->isParameter() && getIrParameter(vd)->arg) { + // Parameters that are part of the LLVM signature will have + // storage associated with them (to handle byref etc.), so + // handle those cases specially by storing a pointer instead + // of a value. + const IrParameter *irparam = getIrParameter(vd); + const bool refout = vd->storage_class & (STCref | STCout); + const bool lazy = vd->storage_class & STClazy; + const bool byref = irparam->arg->byref; + const bool isVthisPtr = irparam->isVthis && !byref; + if (!(refout || (byref && !lazy)) || isVthisPtr) { + // This will be copied to the nesting frame. + if (lazy) + t = irparam->value->getType()->getContainedType(0); + else + t = DtoMemType(vd->type); + } else + t = irparam->value->getType(); + } else if (isSpecialRefVar(vd)) + t = DtoType(vd->type->pointerTo()); + else + t = DtoMemType(vd->type); + + builder.addType(t, getTypeAllocSize(t)); + + IF_LOG Logger::cout() << "Nested var '" << vd->toChars() << "' of type " + << *t << "\n"; + } + + LLStructType *frameType = + LLStructType::create(gIR->context(), builder.defaultTypes(), + std::string("nest.") + fd->toChars()); + + IF_LOG Logger::cout() << "frameType = " << *frameType << '\n'; + + // Store type in IrFunction + irFunc.frameType = frameType; + irFunc.frameTypeAlignment = builder.overallAlignment(); + } else // no captured variables + { + if (parentFunc) { + // Propagate context arg properties if the context arg is passed on + // unmodified. + IrFunction &parentIrFunc = *getIrFunc(parentFunc); + irFunc.frameType = parentIrFunc.frameType; + irFunc.frameTypeAlignment = parentIrFunc.frameTypeAlignment; + irFunc.depth = parentIrFunc.depth; + } + } } +void DtoCreateNestedContext(FuncDeclaration *fd) { + IF_LOG Logger::println("DtoCreateNestedContext for %s", fd->toPrettyChars()); + LOG_SCOPE -void DtoCreateNestedContext(FuncDeclaration* fd) { - IF_LOG Logger::println("DtoCreateNestedContext for %s", fd->toPrettyChars()); - LOG_SCOPE + DtoCreateNestedContextType(fd); - DtoCreateNestedContextType(fd); + // construct nested variables array + if (fd->closureVars.dim > 0) { + IrFunction *irfunction = getIrFunc(fd); + unsigned depth = irfunction->depth; + LLStructType *frameType = irfunction->frameType; + // Create frame for current function and append to frames list + LLValue *frame = 0; + bool needsClosure = fd->needsClosure(); + if (needsClosure) { + // FIXME: alignment ? + frame = DtoGcMalloc(fd->loc, frameType, ".frame"); + } else { + unsigned alignment = + std::max(getABITypeAlign(frameType), irfunction->frameTypeAlignment); + frame = DtoRawAlloca(frameType, alignment, ".frame"); + } - // construct nested variables array - if (fd->closureVars.dim > 0) - { - IrFunction* irfunction = getIrFunc(fd); - unsigned depth = irfunction->depth; - LLStructType *frameType = irfunction->frameType; - // Create frame for current function and append to frames list - LLValue* frame = 0; - bool needsClosure = fd->needsClosure(); - if (needsClosure) - { - // FIXME: alignment ? - frame = DtoGcMalloc(fd->loc, frameType, ".frame"); - } + // copy parent frames into beginning + if (depth != 0) { + LLValue *src = irfunction->nestArg; + if (!src) { + assert(irfunction->thisArg); + assert(fd->isMember2()); + LLValue *thisval = DtoLoad(irfunction->thisArg); + AggregateDeclaration *cd = fd->isMember2(); + assert(cd); + assert(cd->vthis); + Logger::println("Indexing to 'this'"); + if (cd->isStructDeclaration()) + src = DtoExtractValue(thisval, getVthisIdx(cd), ".vthis"); else - { - unsigned alignment = std::max(getABITypeAlign(frameType), irfunction->frameTypeAlignment); - frame = DtoRawAlloca(frameType, alignment, ".frame"); - } - - // copy parent frames into beginning - if (depth != 0) { - LLValue* src = irfunction->nestArg; - if (!src) { - assert(irfunction->thisArg); - assert(fd->isMember2()); - LLValue* thisval = DtoLoad(irfunction->thisArg); - AggregateDeclaration* cd = fd->isMember2(); - assert(cd); - assert(cd->vthis); - Logger::println("Indexing to 'this'"); - if (cd->isStructDeclaration()) - src = DtoExtractValue(thisval, getVthisIdx(cd), ".vthis"); - else - src = DtoLoad(DtoGEPi(thisval, 0, getVthisIdx(cd), ".vthis")); - } else { - src = DtoLoad(src); - } - if (depth > 1) { - src = DtoBitCast(src, getVoidPtrType()); - LLValue* dst = DtoBitCast(frame, getVoidPtrType()); - DtoMemCpy(dst, src, DtoConstSize_t((depth-1) * Target::ptrsize), - getABITypeAlign(getVoidPtrType())); - } - // Copy nestArg into framelist; the outer frame is not in the list of pointers - src = DtoBitCast(src, frameType->getContainedType(depth-1)); - LLValue* gep = DtoGEPi(frame, 0, depth-1); - DtoAlignedStore(src, gep); - } - - // store context in IrFunction - irfunction->nestedVar = frame; - - // go through all nested vars and assign addresses where possible. - for (auto vd : fd->closureVars) { - if (needsClosure && vd->needsAutoDtor()) { - // This should really be a front-end, not a glue layer error, - // but we need to fix this in DMD too. - vd->error("has scoped destruction, cannot build closure"); - } - - IrLocal *irLocal = getIrLocal(vd); - LLValue* gep = DtoGEPi(frame, 0, irLocal->nestedIndex, vd->toChars()); - if (vd->isParameter()) { - IF_LOG Logger::println("nested param: %s", vd->toChars()); - LOG_SCOPE - IrParameter* parm = getIrParameter(vd); - - if (parm->arg && parm->arg->byref) - { - storeVariable(vd, gep); - } - else - { - Logger::println("Copying to nested frame"); - // The parameter value is an alloca'd stack slot. - // Copy to the nesting frame and leave the alloca for - // the optimizers to clean up. - DtoStore(DtoLoad(parm->value), gep); - gep->takeName(parm->value); - parm->value = gep; - } - } else { - IF_LOG Logger::println("nested var: %s", vd->toChars()); - assert(!irLocal->value); - irLocal->value = gep; - } - - if (global.params.symdebug) { -#if LDC_LLVM_VER >= 306 - LLSmallVector addr; -#else - LLSmallVector addr; -#endif - gIR->DBuilder.OpOffset(addr, frameType, irLocal->nestedIndex); - gIR->DBuilder.EmitLocalVariable(gep, vd, 0, false, addr); - } - } + src = DtoLoad(DtoGEPi(thisval, 0, getVthisIdx(cd), ".vthis")); + } else { + src = DtoLoad(src); + } + if (depth > 1) { + src = DtoBitCast(src, getVoidPtrType()); + LLValue *dst = DtoBitCast(frame, getVoidPtrType()); + DtoMemCpy(dst, src, DtoConstSize_t((depth - 1) * Target::ptrsize), + getABITypeAlign(getVoidPtrType())); + } + // Copy nestArg into framelist; the outer frame is not in the list of + // pointers + src = DtoBitCast(src, frameType->getContainedType(depth - 1)); + LLValue *gep = DtoGEPi(frame, 0, depth - 1); + DtoAlignedStore(src, gep); } + + // store context in IrFunction + irfunction->nestedVar = frame; + + // go through all nested vars and assign addresses where possible. + for (auto vd : fd->closureVars) { + if (needsClosure && vd->needsAutoDtor()) { + // This should really be a front-end, not a glue layer error, + // but we need to fix this in DMD too. + vd->error("has scoped destruction, cannot build closure"); + } + + IrLocal *irLocal = getIrLocal(vd); + LLValue *gep = DtoGEPi(frame, 0, irLocal->nestedIndex, vd->toChars()); + if (vd->isParameter()) { + IF_LOG Logger::println("nested param: %s", vd->toChars()); + LOG_SCOPE + IrParameter *parm = getIrParameter(vd); + + if (parm->arg && parm->arg->byref) { + storeVariable(vd, gep); + } else { + Logger::println("Copying to nested frame"); + // The parameter value is an alloca'd stack slot. + // Copy to the nesting frame and leave the alloca for + // the optimizers to clean up. + DtoStore(DtoLoad(parm->value), gep); + gep->takeName(parm->value); + parm->value = gep; + } + } else { + IF_LOG Logger::println("nested var: %s", vd->toChars()); + assert(!irLocal->value); + irLocal->value = gep; + } + + if (global.params.symdebug) { +#if LDC_LLVM_VER >= 306 + LLSmallVector addr; +#else + LLSmallVector addr; +#endif + gIR->DBuilder.OpOffset(addr, frameType, irLocal->nestedIndex); + gIR->DBuilder.EmitLocalVariable(gep, vd, 0, false, addr); + } + } + } } diff --git a/gen/nested.h b/gen/nested.h index 5d55e2db5b..d1f74eb645 100644 --- a/gen/nested.h +++ b/gen/nested.h @@ -25,16 +25,18 @@ /////////////////////////////////////////////////////////// /// Creates the context value for a nested function. -void DtoCreateNestedContext(FuncDeclaration* fd); +void DtoCreateNestedContext(FuncDeclaration *fd); /// Resolves the nested context for classes and structs with arbitrary nesting. -void DtoResolveNestedContext(Loc& loc, AggregateDeclaration *decl, LLValue *value); +void DtoResolveNestedContext(Loc &loc, AggregateDeclaration *decl, + LLValue *value); /// Gets the context value for a call to a nested function or creating a nested /// class or struct with arbitrary nesting. -llvm::Value* DtoNestedContext(Loc& loc, Dsymbol* sym); +llvm::Value *DtoNestedContext(Loc &loc, Dsymbol *sym); /// Gets the DValue of a nested variable with arbitrary nesting. -DValue* DtoNestedVariable(Loc& loc, Type* astype, VarDeclaration* vd, bool byref = false); +DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd, + bool byref = false); #endif diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index 0b52869017..a470c443a8 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #include "gen/optimizer.h" -#include "mars.h" // error() +#include "mars.h" // error() #include "gen/cl_helpers.h" #include "gen/logger.h" #include "gen/passes/Passes.h" @@ -37,21 +37,19 @@ #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" -extern llvm::TargetMachine* gTargetMachine; +extern llvm::TargetMachine *gTargetMachine; using namespace llvm; // Allow the user to specify specific optimizations to run. -static cl::list - passList( - cl::desc("Running specific optimizations:"), - cl::Hidden // to clean up --help output - ); +static cl::list + passList(cl::desc("Running specific optimizations:"), + cl::Hidden // to clean up --help output + ); static cl::opt optimizeLevel( - cl::desc("Setting the optimization level:"), - cl::ZeroOrMore, + cl::desc("Setting the optimization level:"), cl::ZeroOrMore, cl::values( - clEnumValN(3, "O", "Equivalent to -O3"), + clEnumValN(3, "O", "Equivalent to -O3"), clEnumValN(0, "O0", "No optimizations (default)"), clEnumValN(1, "O1", "Simple optimizations"), clEnumValN(2, "O2", "Good optimizations"), @@ -63,124 +61,121 @@ static cl::opt optimizeLevel( clEnumValEnd), cl::init(0)); -static cl::opt -noVerify("disable-verify", - cl::desc("Do not verify result module"), - cl::Hidden); +static cl::opt noVerify("disable-verify", + cl::desc("Do not verify result module"), + cl::Hidden); static cl::opt -verifyEach("verify-each", - cl::desc("Run verifier after D-specific and explicitly specified optimization passes"), - cl::Hidden, - cl::ZeroOrMore); + verifyEach("verify-each", + cl::desc("Run verifier after D-specific and explicitly " + "specified optimization passes"), + cl::Hidden, cl::ZeroOrMore); static cl::opt -disableLangSpecificPasses("disable-d-passes", - cl::desc("Disable all D-specific passes"), - cl::ZeroOrMore); + disableLangSpecificPasses("disable-d-passes", + cl::desc("Disable all D-specific passes"), + cl::ZeroOrMore); -static cl::opt -disableSimplifyDruntimeCalls("disable-simplify-drtcalls", - cl::desc("Disable simplification of druntime calls"), - cl::ZeroOrMore); +static cl::opt disableSimplifyDruntimeCalls( + "disable-simplify-drtcalls", + cl::desc("Disable simplification of druntime calls"), cl::ZeroOrMore); -static cl::opt -disableSimplifyLibCalls("disable-simplify-libcalls", +static cl::opt disableSimplifyLibCalls( + "disable-simplify-libcalls", cl::desc("Disable simplification of well-known C runtime calls"), cl::ZeroOrMore); -static cl::opt -disableGCToStack("disable-gc2stack", +static cl::opt disableGCToStack( + "disable-gc2stack", cl::desc("Disable promotion of GC allocations to stack memory"), cl::ZeroOrMore); -static cl::opt > -enableInlining("inlining", - cl::desc("Enable function inlining (default in -O2 and higher)"), - cl::ZeroOrMore); +static cl::opt> + enableInlining( + "inlining", + cl::desc("Enable function inlining (default in -O2 and higher)"), + cl::ZeroOrMore); -static cl::opt -unitAtATime("unit-at-a-time", - cl::desc("Enable basic IPO"), - cl::init(true)); +static cl::opt unitAtATime("unit-at-a-time", cl::desc("Enable basic IPO"), + cl::init(true)); -static cl::opt -stripDebug("strip-debug", - cl::desc("Strip symbolic debug information before optimization")); +static cl::opt stripDebug( + "strip-debug", + cl::desc("Strip symbolic debug information before optimization")); -cl::opt opts::sanitize("sanitize", - cl::desc("Enable runtime instrumentation for bug detection"), +cl::opt opts::sanitize( + "sanitize", cl::desc("Enable runtime instrumentation for bug detection"), cl::init(opts::None), - cl::values( - clEnumValN(opts::AddressSanitizer, "address", "memory errors"), - clEnumValN(opts::MemorySanitizer, "memory", "memory errors"), - clEnumValN(opts::ThreadSanitizer, "thread", "race detection"), - clEnumValEnd)); + cl::values(clEnumValN(opts::AddressSanitizer, "address", "memory errors"), + clEnumValN(opts::MemorySanitizer, "memory", "memory errors"), + clEnumValN(opts::ThreadSanitizer, "thread", "race detection"), + clEnumValEnd)); + +static cl::opt disableLoopUnrolling( + "disable-loop-unrolling", + cl::desc("Disable loop unrolling in all relevant passes"), cl::init(false)); +static cl::opt + disableLoopVectorization("disable-loop-vectorization", + cl::desc("Disable the loop vectorization pass"), + cl::init(false)); static cl::opt -disableLoopUnrolling("disable-loop-unrolling", - cl::desc("Disable loop unrolling in all relevant passes"), - cl::init(false)); -static cl::opt -disableLoopVectorization("disable-loop-vectorization", - cl::desc("Disable the loop vectorization pass"), - cl::init(false)); - -static cl::opt -disableSLPVectorization("disable-slp-vectorization", - cl::desc("Disable the slp vectorization pass"), - cl::init(false)); + disableSLPVectorization("disable-slp-vectorization", + cl::desc("Disable the slp vectorization pass"), + cl::init(false)); static unsigned optLevel() { - // Use -O2 as a base for the size-optimization levels. - return optimizeLevel >= 0 ? optimizeLevel : 2; + // Use -O2 as a base for the size-optimization levels. + return optimizeLevel >= 0 ? optimizeLevel : 2; } -static unsigned sizeLevel() { - return optimizeLevel < 0 ? -optimizeLevel : 0; -} +static unsigned sizeLevel() { return optimizeLevel < 0 ? -optimizeLevel : 0; } // Determines whether or not to run the normal, full inlining pass. bool willInline() { - return enableInlining == cl::BOU_TRUE || - (enableInlining == cl::BOU_UNSET && optLevel() > 1); + return enableInlining == cl::BOU_TRUE || + (enableInlining == cl::BOU_UNSET && optLevel() > 1); } -bool isOptimizationEnabled() { - return optimizeLevel != 0; -} +bool isOptimizationEnabled() { return optimizeLevel != 0; } llvm::CodeGenOpt::Level codeGenOptLevel() { - const int opt = optLevel(); - // Use same appoach as clang (see lib/CodeGen/BackendUtil.cpp) - llvm::CodeGenOpt::Level codeGenOptLevel = llvm::CodeGenOpt::Default; - // Debug info doesn't work properly with CodeGenOpt <> None - if (global.params.symdebug || !opt) codeGenOptLevel = llvm::CodeGenOpt::None; - else if (opt >= 3) codeGenOptLevel = llvm::CodeGenOpt::Aggressive; - return codeGenOptLevel; + const int opt = optLevel(); + // Use same appoach as clang (see lib/CodeGen/BackendUtil.cpp) + llvm::CodeGenOpt::Level codeGenOptLevel = llvm::CodeGenOpt::Default; + // Debug info doesn't work properly with CodeGenOpt <> None + if (global.params.symdebug || !opt) + codeGenOptLevel = llvm::CodeGenOpt::None; + else if (opt >= 3) + codeGenOptLevel = llvm::CodeGenOpt::Aggressive; + return codeGenOptLevel; } -static inline void addPass(PassManagerBase& pm, Pass* pass) { - pm.add(pass); +static inline void addPass(PassManagerBase &pm, Pass *pass) { + pm.add(pass); - if (verifyEach) pm.add(createVerifierPass()); + if (verifyEach) + pm.add(createVerifierPass()); } -static void addStripExternalsPass(const PassManagerBuilder &builder, PassManagerBase &pm) { - if (builder.OptLevel >= 1) { - addPass(pm, createStripExternalsPass()); - addPass(pm, createGlobalDCEPass()); - } +static void addStripExternalsPass(const PassManagerBuilder &builder, + PassManagerBase &pm) { + if (builder.OptLevel >= 1) { + addPass(pm, createStripExternalsPass()); + addPass(pm, createGlobalDCEPass()); + } } -static void addSimplifyDRuntimeCallsPass(const PassManagerBuilder &builder, PassManagerBase &pm) { - if (builder.OptLevel >= 2 && builder.SizeLevel == 0) - addPass(pm, createSimplifyDRuntimeCalls()); +static void addSimplifyDRuntimeCallsPass(const PassManagerBuilder &builder, + PassManagerBase &pm) { + if (builder.OptLevel >= 2 && builder.SizeLevel == 0) + addPass(pm, createSimplifyDRuntimeCalls()); } -static void addGarbageCollect2StackPass(const PassManagerBuilder &builder, PassManagerBase &pm) { - if (builder.OptLevel >= 2 && builder.SizeLevel == 0) - addPass(pm, createGarbageCollect2Stack()); +static void addGarbageCollect2StackPass(const PassManagerBuilder &builder, + PassManagerBase &pm) { + if (builder.OptLevel >= 2 && builder.SizeLevel == 0) + addPass(pm, createGarbageCollect2Stack()); } static void addAddressSanitizerPasses(const PassManagerBuilder &Builder, @@ -219,217 +214,225 @@ static void addThreadSanitizerPass(const PassManagerBuilder &Builder, * PassManagerBuilder. */ #if LDC_LLVM_VER >= 307 -static void addOptimizationPasses(legacy::PassManagerBase &mpm, legacy::FunctionPassManager &fpm, +static void addOptimizationPasses(legacy::PassManagerBase &mpm, + legacy::FunctionPassManager &fpm, #else -static void addOptimizationPasses(PassManagerBase &mpm, FunctionPassManager &fpm, +static void addOptimizationPasses(PassManagerBase &mpm, + FunctionPassManager &fpm, #endif unsigned optLevel, unsigned sizeLevel) { - fpm.add(createVerifierPass()); // Verify that input is correct + fpm.add(createVerifierPass()); // Verify that input is correct - PassManagerBuilder builder; - builder.OptLevel = optLevel; - builder.SizeLevel = sizeLevel; + PassManagerBuilder builder; + builder.OptLevel = optLevel; + builder.SizeLevel = sizeLevel; - if (willInline()) { - unsigned threshold = 225; - if (sizeLevel == 1) // -Os - threshold = 75; - else if (sizeLevel == 2) // -Oz - threshold = 25; - if (optLevel > 2) - threshold = 275; - builder.Inliner = createFunctionInliningPass(threshold); - } else { - builder.Inliner = createAlwaysInlinerPass(); - } - builder.DisableUnitAtATime = !unitAtATime; - builder.DisableUnrollLoops = optLevel == 0; + if (willInline()) { + unsigned threshold = 225; + if (sizeLevel == 1) // -Os + threshold = 75; + else if (sizeLevel == 2) // -Oz + threshold = 25; + if (optLevel > 2) + threshold = 275; + builder.Inliner = createFunctionInliningPass(threshold); + } else { + builder.Inliner = createAlwaysInlinerPass(); + } + builder.DisableUnitAtATime = !unitAtATime; + builder.DisableUnrollLoops = optLevel == 0; - builder.DisableUnrollLoops = (disableLoopUnrolling.getNumOccurrences() > 0) ? - disableLoopUnrolling : optLevel == 0; + builder.DisableUnrollLoops = (disableLoopUnrolling.getNumOccurrences() > 0) + ? disableLoopUnrolling + : optLevel == 0; - // This is final, unless there is a #pragma vectorize enable - if (disableLoopVectorization) - builder.LoopVectorize = false; - // If option wasn't forced via cmd line (-vectorize-loops, -loop-vectorize) - else if (!builder.LoopVectorize) - builder.LoopVectorize = optLevel > 1 && sizeLevel < 2; + // This is final, unless there is a #pragma vectorize enable + if (disableLoopVectorization) + builder.LoopVectorize = false; + // If option wasn't forced via cmd line (-vectorize-loops, -loop-vectorize) + else if (!builder.LoopVectorize) + builder.LoopVectorize = optLevel > 1 && sizeLevel < 2; - // When #pragma vectorize is on for SLP, do the same as above - builder.SLPVectorize = - disableSLPVectorization ? false : optLevel > 1 && sizeLevel < 2; + // When #pragma vectorize is on for SLP, do the same as above + builder.SLPVectorize = + disableSLPVectorization ? false : optLevel > 1 && sizeLevel < 2; - if (opts::sanitize == opts::AddressSanitizer) { - builder.addExtension(PassManagerBuilder::EP_OptimizerLast, - addAddressSanitizerPasses); - builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, - addAddressSanitizerPasses); - } + if (opts::sanitize == opts::AddressSanitizer) { + builder.addExtension(PassManagerBuilder::EP_OptimizerLast, + addAddressSanitizerPasses); + builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, + addAddressSanitizerPasses); + } - if (opts::sanitize == opts::MemorySanitizer) { - builder.addExtension(PassManagerBuilder::EP_OptimizerLast, - addMemorySanitizerPass); - builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, - addMemorySanitizerPass); - } + if (opts::sanitize == opts::MemorySanitizer) { + builder.addExtension(PassManagerBuilder::EP_OptimizerLast, + addMemorySanitizerPass); + builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, + addMemorySanitizerPass); + } - if (opts::sanitize == opts::ThreadSanitizer) { - builder.addExtension(PassManagerBuilder::EP_OptimizerLast, - addThreadSanitizerPass); - builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, - addThreadSanitizerPass); - } + if (opts::sanitize == opts::ThreadSanitizer) { + builder.addExtension(PassManagerBuilder::EP_OptimizerLast, + addThreadSanitizerPass); + builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, + addThreadSanitizerPass); + } - if (!disableLangSpecificPasses) { - if (!disableSimplifyDruntimeCalls) - builder.addExtension(PassManagerBuilder::EP_LoopOptimizerEnd, addSimplifyDRuntimeCallsPass); + if (!disableLangSpecificPasses) { + if (!disableSimplifyDruntimeCalls) + builder.addExtension(PassManagerBuilder::EP_LoopOptimizerEnd, + addSimplifyDRuntimeCallsPass); - if (!disableGCToStack) - builder.addExtension(PassManagerBuilder::EP_LoopOptimizerEnd, addGarbageCollect2StackPass); - } + if (!disableGCToStack) + builder.addExtension(PassManagerBuilder::EP_LoopOptimizerEnd, + addGarbageCollect2StackPass); + } - // EP_OptimizerLast does not exist in LLVM 3.0, add it manually below. - builder.addExtension(PassManagerBuilder::EP_OptimizerLast, addStripExternalsPass); + // EP_OptimizerLast does not exist in LLVM 3.0, add it manually below. + builder.addExtension(PassManagerBuilder::EP_OptimizerLast, + addStripExternalsPass); - builder.populateFunctionPassManager(fpm); - builder.populateModulePassManager(mpm); + builder.populateFunctionPassManager(fpm); + builder.populateModulePassManager(mpm); } ////////////////////////////////////////////////////////////////////////////////////////// // This function runs optimization passes based on command line arguments. // Returns true if any optimization passes were invoked. -bool ldc_optimize_module(llvm::Module *M) -{ - // Create a PassManager to hold and optimize the collection of - // per-module passes we are about to build. +bool ldc_optimize_module(llvm::Module *M) { +// Create a PassManager to hold and optimize the collection of +// per-module passes we are about to build. #if LDC_LLVM_VER >= 307 - legacy:: + legacy:: #endif - PassManager mpm; + PassManager mpm; #if LDC_LLVM_VER >= 307 - // Add an appropriate TargetLibraryInfo pass for the module's triple. - TargetLibraryInfoImpl *tlii = new TargetLibraryInfoImpl(Triple(M->getTargetTriple())); + // Add an appropriate TargetLibraryInfo pass for the module's triple. + TargetLibraryInfoImpl *tlii = + new TargetLibraryInfoImpl(Triple(M->getTargetTriple())); - // The -disable-simplify-libcalls flag actually disables all builtin optzns. - if (disableSimplifyLibCalls) - tlii->disableAllFunctions(); + // The -disable-simplify-libcalls flag actually disables all builtin optzns. + if (disableSimplifyLibCalls) + tlii->disableAllFunctions(); - mpm.add(new TargetLibraryInfoWrapperPass(*tlii)); + mpm.add(new TargetLibraryInfoWrapperPass(*tlii)); #else - // Add an appropriate TargetLibraryInfo pass for the module's triple. - TargetLibraryInfo *tli = new TargetLibraryInfo(Triple(M->getTargetTriple())); + // Add an appropriate TargetLibraryInfo pass for the module's triple. + TargetLibraryInfo *tli = new TargetLibraryInfo(Triple(M->getTargetTriple())); - // The -disable-simplify-libcalls flag actually disables all builtin optzns. - if (disableSimplifyLibCalls) - tli->disableAllFunctions(); + // The -disable-simplify-libcalls flag actually disables all builtin optzns. + if (disableSimplifyLibCalls) + tli->disableAllFunctions(); - mpm.add(tli); + mpm.add(tli); #endif - // Add an appropriate DataLayout instance for this module. +// Add an appropriate DataLayout instance for this module. #if LDC_LLVM_VER >= 307 - // The DataLayout is already set at the module (in module.cpp, - // method Module::genLLVMModule()) - // FIXME: Introduce new command line switch default-data-layout to - // override the module data layout +// The DataLayout is already set at the module (in module.cpp, +// method Module::genLLVMModule()) +// FIXME: Introduce new command line switch default-data-layout to +// override the module data layout #elif LDC_LLVM_VER == 306 - mpm.add(new DataLayoutPass()); + mpm.add(new DataLayoutPass()); #else - const DataLayout *DL = M->getDataLayout(); - assert(DL && "DataLayout not set at module"); - mpm.add(new DataLayoutPass(*DL)); + const DataLayout *DL = M->getDataLayout(); + assert(DL && + "DataLayout not set at module"); + mpm.add(new DataLayoutPass(*DL)); #endif #if LDC_LLVM_VER >= 307 - // Add internal analysis passes from the target machine. - mpm.add(createTargetTransformInfoWrapperPass(gTargetMachine->getTargetIRAnalysis())); + // Add internal analysis passes from the target machine. + mpm.add(createTargetTransformInfoWrapperPass( + gTargetMachine->getTargetIRAnalysis())); #else - // Add internal analysis passes from the target machine. - gTargetMachine->addAnalysisPasses(mpm); + // Add internal analysis passes from the target machine. + gTargetMachine->addAnalysisPasses(mpm); #endif - // Also set up a manager for the per-function passes. +// Also set up a manager for the per-function passes. #if LDC_LLVM_VER >= 307 - legacy:: + legacy:: #endif - FunctionPassManager fpm(M); + FunctionPassManager fpm(M); #if LDC_LLVM_VER >= 307 - // Add internal analysis passes from the target machine. - fpm.add(createTargetTransformInfoWrapperPass(gTargetMachine->getTargetIRAnalysis())); + // Add internal analysis passes from the target machine. + fpm.add(createTargetTransformInfoWrapperPass( + gTargetMachine->getTargetIRAnalysis())); #elif LDC_LLVM_VER >= 306 - fpm.add(new DataLayoutPass()); - gTargetMachine->addAnalysisPasses(fpm); + fpm.add(new DataLayoutPass()); + gTargetMachine->addAnalysisPasses(fpm); #else - fpm.add(new DataLayoutPass(M)); - gTargetMachine->addAnalysisPasses(fpm); + fpm.add(new DataLayoutPass(M)); + gTargetMachine->addAnalysisPasses(fpm); #endif - // If the -strip-debug command line option was specified, add it before - // anything else. - if (stripDebug) - mpm.add(createStripSymbolsPass(true)); + // If the -strip-debug command line option was specified, add it before + // anything else. + if (stripDebug) + mpm.add(createStripSymbolsPass(true)); - bool defaultsAdded = false; - // Create a new optimization pass for each one specified on the command line - for (unsigned i = 0; i < passList.size(); ++i) { - if (optimizeLevel && optimizeLevel.getPosition() < passList.getPosition(i)) { - addOptimizationPasses(mpm, fpm, optLevel(), sizeLevel()); - defaultsAdded = true; - } - - const PassInfo *passInf = passList[i]; - Pass *pass = 0; - if (passInf->getNormalCtor()) - pass = passInf->getNormalCtor()(); - else { - const char* arg = passInf->getPassArgument(); // may return null - if (arg) - error(Loc(), "Can't create pass '-%s' (%s)", arg, pass->getPassName()); - else - error(Loc(), "Can't create pass (%s)", pass->getPassName()); - llvm_unreachable("pass creation failed"); - } - if (pass) { - addPass(mpm, pass); - } + bool defaultsAdded = false; + // Create a new optimization pass for each one specified on the command line + for (unsigned i = 0; i < passList.size(); ++i) { + if (optimizeLevel && + optimizeLevel.getPosition() < passList.getPosition(i)) { + addOptimizationPasses(mpm, fpm, optLevel(), sizeLevel()); + defaultsAdded = true; } - // Add the default passes for the specified optimization level. - if (!defaultsAdded) - addOptimizationPasses(mpm, fpm, optLevel(), sizeLevel()); + const PassInfo *passInf = passList[i]; + Pass *pass = 0; + if (passInf->getNormalCtor()) + pass = passInf->getNormalCtor()(); + else { + const char *arg = passInf->getPassArgument(); // may return null + if (arg) + error(Loc(), "Can't create pass '-%s' (%s)", arg, pass->getPassName()); + else + error(Loc(), "Can't create pass (%s)", pass->getPassName()); + llvm_unreachable("pass creation failed"); + } + if (pass) { + addPass(mpm, pass); + } + } - // Run per-function passes. - fpm.doInitialization(); - for (auto& F : *M) - fpm.run(F); - fpm.doFinalization(); + // Add the default passes for the specified optimization level. + if (!defaultsAdded) + addOptimizationPasses(mpm, fpm, optLevel(), sizeLevel()); - // Run per-module passes. - mpm.run(*M); + // Run per-function passes. + fpm.doInitialization(); + for (auto &F : *M) + fpm.run(F); + fpm.doFinalization(); - // Verify the resulting module. - verifyModule(M); + // Run per-module passes. + mpm.run(*M); - // Report that we run some passes. - return true; + // Verify the resulting module. + verifyModule(M); + + // Report that we run some passes. + return true; } // Verifies the module. -void verifyModule(llvm::Module* m) { - if (!noVerify) { - Logger::println("Verifying module..."); - LOG_SCOPE; - std::string ErrorStr; - raw_string_ostream OS(ErrorStr); - if (llvm::verifyModule(*m, &OS)) - { - error(Loc(), "%s", ErrorStr.c_str()); - fatal(); - } - else { - Logger::println("Verification passed!"); - } +void verifyModule(llvm::Module *m) { + if (!noVerify) { + Logger::println("Verifying module..."); + LOG_SCOPE; + std::string ErrorStr; + raw_string_ostream OS(ErrorStr); + if (llvm::verifyModule(*m, &OS)) { + error(Loc(), "%s", ErrorStr.c_str()); + fatal(); + } else { + Logger::println("Verification passed!"); } + } } diff --git a/gen/optimizer.h b/gen/optimizer.h index 9d2e225cb5..5e1bce4421 100644 --- a/gen/optimizer.h +++ b/gen/optimizer.h @@ -22,14 +22,21 @@ namespace opts { -enum SanitizerCheck { None, AddressSanitizer, MemorySanitizer, ThreadSanitizer }; +enum SanitizerCheck { + None, + AddressSanitizer, + MemorySanitizer, + ThreadSanitizer +}; extern llvm::cl::opt sanitize; } -namespace llvm { class Module; } +namespace llvm { +class Module; +} -bool ldc_optimize_module(llvm::Module* m); +bool ldc_optimize_module(llvm::Module *m); // Returns whether the normal, full inlining pass will be run. bool willInline(); @@ -38,7 +45,6 @@ bool isOptimizationEnabled(); llvm::CodeGenOpt::Level codeGenOptLevel(); -void verifyModule(llvm::Module* m); +void verifyModule(llvm::Module *m); #endif - diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index 85af41a7bd..4a8d051d39 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -45,363 +45,368 @@ using namespace llvm; STATISTIC(NumGcToStack, "Number of calls promoted to constant-size allocas"); -STATISTIC(NumToDynSize, "Number of calls promoted to dynamically-sized allocas"); -STATISTIC(NumDeleted, "Number of GC calls deleted because the return value was unused"); +STATISTIC(NumToDynSize, + "Number of calls promoted to dynamically-sized allocas"); +STATISTIC(NumDeleted, + "Number of GC calls deleted because the return value was unused"); static cl::opt -SizeLimit("dgc2stack-size-limit", cl::init(1024), cl::Hidden, - cl::desc("Require allocs to be smaller than n bytes to be promoted, 0 to ignore.")); + SizeLimit("dgc2stack-size-limit", cl::init(1024), cl::Hidden, + cl::desc("Require allocs to be smaller than n bytes to be " + "promoted, 0 to ignore.")); namespace { - struct Analysis { - const DataLayout& DL; - const Module& M; - CallGraph* CG; - CallGraphNode* CGNode; +struct Analysis { + const DataLayout &DL; + const Module &M; + CallGraph *CG; + CallGraphNode *CGNode; - Type* getTypeFor(Value* typeinfo) const; - }; + Type *getTypeFor(Value *typeinfo) const; +}; } //===----------------------------------------------------------------------===// // Helper functions //===----------------------------------------------------------------------===// -void EmitMemSet(IRBuilder<>& B, Value* Dst, Value* Val, Value* Len, - const Analysis& A) { - Dst = B.CreateBitCast(Dst, PointerType::getUnqual(B.getInt8Ty())); +void EmitMemSet(IRBuilder<> &B, Value *Dst, Value *Val, Value *Len, + const Analysis &A) { + Dst = B.CreateBitCast(Dst, PointerType::getUnqual(B.getInt8Ty())); - CallSite CS = B.CreateMemSet(Dst, Val, Len, 1 /*Align*/, false /*isVolatile*/); - if (A.CGNode) - A.CGNode->addCalledFunction(CS, A.CG->getOrInsertFunction(CS.getCalledFunction())); + CallSite CS = + B.CreateMemSet(Dst, Val, Len, 1 /*Align*/, false /*isVolatile*/); + if (A.CGNode) + A.CGNode->addCalledFunction( + CS, A.CG->getOrInsertFunction(CS.getCalledFunction())); } -static void EmitMemZero(IRBuilder<>& B, Value* Dst, Value* Len, - const Analysis& A) { - EmitMemSet(B, Dst, ConstantInt::get(B.getInt8Ty(), 0), Len, A); +static void EmitMemZero(IRBuilder<> &B, Value *Dst, Value *Len, + const Analysis &A) { + EmitMemSet(B, Dst, ConstantInt::get(B.getInt8Ty(), 0), Len, A); } - //===----------------------------------------------------------------------===// // Helpers for specific types of GC calls. //===----------------------------------------------------------------------===// namespace { - namespace ReturnType { - enum Type { - Pointer, /// Function returns a pointer to the allocated memory. - Array /// Function returns the allocated memory as an array slice. - }; - } - - class FunctionInfo { - protected: - Type* Ty; - - public: - ReturnType::Type ReturnType; - - // Analyze the current call, filling in some fields. Returns true if - // this is an allocation we can stack-allocate. - virtual bool analyze(CallSite CS, const Analysis& A) = 0; - - // Returns the alloca to replace this call. - // It will always be inserted before the call. - virtual Value* promote(CallSite CS, IRBuilder<>& B, const Analysis& A) { - NumGcToStack++; - - Instruction* Begin = CS.getCaller()->getEntryBlock().begin(); - return new AllocaInst(Ty, ".nongc_mem", Begin); // FIXME: align? - } - - FunctionInfo(ReturnType::Type returnType) - : ReturnType(returnType) {} - virtual ~FunctionInfo() {} - }; - - static bool isKnownLessThan(Value* Val, uint64_t Limit, const Analysis& A) { - unsigned BitsLimit = Log2_64(Limit); - - // LLVM's alloca ueses an i32 for the number of elements. - BitsLimit = std::min(BitsLimit, 32U); - - const IntegerType* SizeType = - dyn_cast(Val->getType()); - if (!SizeType) - return false; - unsigned Bits = SizeType->getBitWidth(); - - if (Bits > BitsLimit) { - APInt Mask = APInt::getLowBitsSet(Bits, BitsLimit); - Mask.flipAllBits(); - APInt KnownZero(Bits, 0), KnownOne(Bits, 0); -#if LDC_LLVM_VER >= 307 - computeKnownBits(Val, KnownZero, KnownOne, A.DL); -#else - computeKnownBits(Val, KnownZero, KnownOne, &A.DL); -#endif - - if ((KnownZero & Mask) != Mask) - return false; - } - - return true; - } - - class TypeInfoFI : public FunctionInfo { - unsigned TypeInfoArgNr; - - public: - TypeInfoFI(ReturnType::Type returnType, unsigned tiArgNr) - : FunctionInfo(returnType), TypeInfoArgNr(tiArgNr) {} - - virtual bool analyze(CallSite CS, const Analysis& A) { - Value* TypeInfo = CS.getArgument(TypeInfoArgNr); - Ty = A.getTypeFor(TypeInfo); - if (!Ty) return false; - return A.DL.getTypeAllocSize(Ty) < SizeLimit; - } - }; - - class ArrayFI : public TypeInfoFI { - int ArrSizeArgNr; - bool Initialized; - Value* arrSize; - - public: - ArrayFI(ReturnType::Type returnType, unsigned tiArgNr, - unsigned arrSizeArgNr, bool initialized) - : TypeInfoFI(returnType, tiArgNr), - ArrSizeArgNr(arrSizeArgNr), - Initialized(initialized) - {} - - virtual bool analyze(CallSite CS, const Analysis& A) { - if (!TypeInfoFI::analyze(CS, A)) return false; - - arrSize = CS.getArgument(ArrSizeArgNr); - - // Extract the element type from the array type. - const StructType* ArrTy = dyn_cast(Ty); - assert(ArrTy && "Dynamic array type not a struct?"); - assert(isa(ArrTy->getElementType(0))); - const PointerType* PtrTy = - cast(ArrTy->getElementType(1)); - Ty = PtrTy->getElementType(); - - // If the user explicitly disabled the limits, don't even check - // whether the element count fits in 32 bits. This could cause - // miscompilations for humongous arrays, but as the value "range" - // (set bits) inference algorithm is rather limited, this is - // useful for experimenting. - if (SizeLimit > 0) { - uint64_t ElemSize = A.DL.getTypeAllocSize(Ty); - if (!isKnownLessThan(arrSize, SizeLimit / ElemSize, A)) - return false; - } - - return true; - } - - virtual Value* promote(CallSite CS, IRBuilder<>& B, const Analysis& A) { - IRBuilder<> Builder = B; - // If the allocation is of constant size it's best to put it in the - // entry block, so do so if we're not already there. - // For dynamically-sized allocations it's best to avoid the overhead - // of allocating them if possible, so leave those where they are. - // While we're at it, update statistics too. - if (isa(arrSize)) { - BasicBlock& Entry = CS.getCaller()->getEntryBlock(); - if (Builder.GetInsertBlock() != &Entry) - Builder.SetInsertPoint(&Entry, Entry.begin()); - NumGcToStack++; - } else { - NumToDynSize++; - } - - // Convert array size to 32 bits if necessary - Value* count = Builder.CreateIntCast(arrSize, Builder.getInt32Ty(), false); - AllocaInst* alloca = Builder.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? - - if (Initialized) { - // For now, only zero-init is supported. - uint64_t size = A.DL.getTypeStoreSize(Ty); - Value* TypeSize = ConstantInt::get(arrSize->getType(), size); - // Use the original B to put initialization at the - // allocation site. - Value* Size = B.CreateMul(TypeSize, arrSize); - EmitMemZero(B, alloca, Size, A); - } - - if (ReturnType == ReturnType::Array) { - Value* arrStruct = llvm::UndefValue::get(CS.getType()); - arrStruct = Builder.CreateInsertValue(arrStruct, arrSize, 0); - Value* memPtr = Builder.CreateBitCast(alloca, - PointerType::getUnqual(B.getInt8Ty())); - arrStruct = Builder.CreateInsertValue(arrStruct, memPtr, 1); - return arrStruct; - } - - return alloca; - } - }; - - // FunctionInfo for _d_newclass - class AllocClassFI : public FunctionInfo { - public: - virtual bool analyze(CallSite CS, const Analysis& A) { - if (CS.arg_size() != 1) - return false; - Value* arg = CS.getArgument(0)->stripPointerCasts(); - GlobalVariable* ClassInfo = dyn_cast(arg); - if (!ClassInfo) - return false; - - std::string metaname = CD_PREFIX; - metaname += ClassInfo->getName(); - - NamedMDNode* meta = A.M.getNamedMetadata(metaname); - if (!meta) - return false; - - MDNode* node = static_cast(meta->getOperand(0)); - if (!node || node->getNumOperands() != CD_NumFields) - return false; - - // Inserting destructor calls is not implemented yet, so classes - // with destructors are ignored for now. -#if LDC_LLVM_VER >= 306 - auto hasDestructor = mdconst::dyn_extract(node->getOperand(CD_Finalize)); -#else - Constant* hasDestructor = dyn_cast(node->getOperand(CD_Finalize)); -#endif - // We can't stack-allocate if the class has a custom deallocator - // (Custom allocators don't get turned into this runtime call, so - // those can be ignored) -#if LDC_LLVM_VER >= 306 - auto hasCustomDelete = mdconst::dyn_extract(node->getOperand(CD_CustomDelete)); -#else - Constant* hasCustomDelete = dyn_cast(node->getOperand(CD_CustomDelete)); -#endif - if (hasDestructor == NULL || hasCustomDelete == NULL) - return false; - - if (ConstantExpr::getOr(hasDestructor, hasCustomDelete) - != ConstantInt::getFalse(A.M.getContext())) - return false; - -#if LDC_LLVM_VER >= 306 - Ty = mdconst::dyn_extract(node->getOperand(CD_BodyType))->getType(); -#else - Ty = node->getOperand(CD_BodyType)->getType(); -#endif - return A.DL.getTypeAllocSize(Ty) < SizeLimit; - } - - // The default promote() should be fine. - - AllocClassFI() : FunctionInfo(ReturnType::Pointer) {} - }; - - /// Describes runtime functions that allocate a chunk of memory with a - /// given size. - class UntypedMemoryFI : public FunctionInfo { - unsigned SizeArgNr; - Value* SizeArg; - public: - virtual bool analyze(CallSite CS, const Analysis& A) { - if (CS.arg_size() < SizeArgNr + 1) - return false; - - SizeArg = CS.getArgument(SizeArgNr); - - // If the user explicitly disabled the limits, don't even check - // whether the allocated size fits in 32 bits. This could cause - // miscompilations for humongous allocations, but as the value - // "range" (set bits) inference algorithm is rather limited, this - // is useful for experimenting. - if (SizeLimit > 0) { - if (!isKnownLessThan(SizeArg, SizeLimit, A)) - return false; - } - - // Should be i8. - Ty = CS.getType()->getContainedType(0); - return true; - } - - virtual Value* promote(CallSite CS, IRBuilder<>& B, const Analysis& A) { - IRBuilder<> Builder = B; - // If the allocation is of constant size it's best to put it in the - // entry block, so do so if we're not already there. - // For dynamically-sized allocations it's best to avoid the overhead - // of allocating them if possible, so leave those where they are. - // While we're at it, update statistics too. - if (isa(SizeArg)) { - BasicBlock& Entry = CS.getCaller()->getEntryBlock(); - if (Builder.GetInsertBlock() != &Entry) - Builder.SetInsertPoint(&Entry, Entry.begin()); - NumGcToStack++; - } else { - NumToDynSize++; - } - - // Convert array size to 32 bits if necessary - Value* count = Builder.CreateIntCast(SizeArg, Builder.getInt32Ty(), false); - AllocaInst* alloca = Builder.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? - - return Builder.CreateBitCast(alloca, CS.getType()); - } - - UntypedMemoryFI(unsigned sizeArgNr) - : FunctionInfo(ReturnType::Pointer), - SizeArgNr(sizeArgNr) - {} - }; +namespace ReturnType { +enum Type { + Pointer, /// Function returns a pointer to the allocated memory. + Array /// Function returns the allocated memory as an array slice. +}; } +class FunctionInfo { +protected: + Type *Ty; + +public: + ReturnType::Type ReturnType; + + // Analyze the current call, filling in some fields. Returns true if + // this is an allocation we can stack-allocate. + virtual bool analyze(CallSite CS, const Analysis &A) = 0; + + // Returns the alloca to replace this call. + // It will always be inserted before the call. + virtual Value *promote(CallSite CS, IRBuilder<> &B, const Analysis &A) { + NumGcToStack++; + + Instruction *Begin = CS.getCaller()->getEntryBlock().begin(); + return new AllocaInst(Ty, ".nongc_mem", Begin); // FIXME: align? + } + + FunctionInfo(ReturnType::Type returnType) : ReturnType(returnType) {} + virtual ~FunctionInfo() {} +}; + +static bool isKnownLessThan(Value *Val, uint64_t Limit, const Analysis &A) { + unsigned BitsLimit = Log2_64(Limit); + + // LLVM's alloca ueses an i32 for the number of elements. + BitsLimit = std::min(BitsLimit, 32U); + + const IntegerType *SizeType = dyn_cast(Val->getType()); + if (!SizeType) + return false; + unsigned Bits = SizeType->getBitWidth(); + + if (Bits > BitsLimit) { + APInt Mask = APInt::getLowBitsSet(Bits, BitsLimit); + Mask.flipAllBits(); + APInt KnownZero(Bits, 0), KnownOne(Bits, 0); +#if LDC_LLVM_VER >= 307 + computeKnownBits(Val, KnownZero, KnownOne, A.DL); +#else + computeKnownBits(Val, KnownZero, KnownOne, &A.DL); +#endif + + if ((KnownZero & Mask) != Mask) + return false; + } + + return true; +} + +class TypeInfoFI : public FunctionInfo { + unsigned TypeInfoArgNr; + +public: + TypeInfoFI(ReturnType::Type returnType, unsigned tiArgNr) + : FunctionInfo(returnType), TypeInfoArgNr(tiArgNr) {} + + virtual bool analyze(CallSite CS, const Analysis &A) { + Value *TypeInfo = CS.getArgument(TypeInfoArgNr); + Ty = A.getTypeFor(TypeInfo); + if (!Ty) + return false; + return A.DL.getTypeAllocSize(Ty) < SizeLimit; + } +}; + +class ArrayFI : public TypeInfoFI { + int ArrSizeArgNr; + bool Initialized; + Value *arrSize; + +public: + ArrayFI(ReturnType::Type returnType, unsigned tiArgNr, unsigned arrSizeArgNr, + bool initialized) + : TypeInfoFI(returnType, tiArgNr), ArrSizeArgNr(arrSizeArgNr), + Initialized(initialized) {} + + virtual bool analyze(CallSite CS, const Analysis &A) { + if (!TypeInfoFI::analyze(CS, A)) + return false; + + arrSize = CS.getArgument(ArrSizeArgNr); + + // Extract the element type from the array type. + const StructType *ArrTy = dyn_cast(Ty); + assert(ArrTy && "Dynamic array type not a struct?"); + assert(isa(ArrTy->getElementType(0))); + const PointerType *PtrTy = cast(ArrTy->getElementType(1)); + Ty = PtrTy->getElementType(); + + // If the user explicitly disabled the limits, don't even check + // whether the element count fits in 32 bits. This could cause + // miscompilations for humongous arrays, but as the value "range" + // (set bits) inference algorithm is rather limited, this is + // useful for experimenting. + if (SizeLimit > 0) { + uint64_t ElemSize = A.DL.getTypeAllocSize(Ty); + if (!isKnownLessThan(arrSize, SizeLimit / ElemSize, A)) + return false; + } + + return true; + } + + virtual Value *promote(CallSite CS, IRBuilder<> &B, const Analysis &A) { + IRBuilder<> Builder = B; + // If the allocation is of constant size it's best to put it in the + // entry block, so do so if we're not already there. + // For dynamically-sized allocations it's best to avoid the overhead + // of allocating them if possible, so leave those where they are. + // While we're at it, update statistics too. + if (isa(arrSize)) { + BasicBlock &Entry = CS.getCaller()->getEntryBlock(); + if (Builder.GetInsertBlock() != &Entry) + Builder.SetInsertPoint(&Entry, Entry.begin()); + NumGcToStack++; + } else { + NumToDynSize++; + } + + // Convert array size to 32 bits if necessary + Value *count = Builder.CreateIntCast(arrSize, Builder.getInt32Ty(), false); + AllocaInst *alloca = + Builder.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? + + if (Initialized) { + // For now, only zero-init is supported. + uint64_t size = A.DL.getTypeStoreSize(Ty); + Value *TypeSize = ConstantInt::get(arrSize->getType(), size); + // Use the original B to put initialization at the + // allocation site. + Value *Size = B.CreateMul(TypeSize, arrSize); + EmitMemZero(B, alloca, Size, A); + } + + if (ReturnType == ReturnType::Array) { + Value *arrStruct = llvm::UndefValue::get(CS.getType()); + arrStruct = Builder.CreateInsertValue(arrStruct, arrSize, 0); + Value *memPtr = + Builder.CreateBitCast(alloca, PointerType::getUnqual(B.getInt8Ty())); + arrStruct = Builder.CreateInsertValue(arrStruct, memPtr, 1); + return arrStruct; + } + + return alloca; + } +}; + +// FunctionInfo for _d_newclass +class AllocClassFI : public FunctionInfo { +public: + virtual bool analyze(CallSite CS, const Analysis &A) { + if (CS.arg_size() != 1) + return false; + Value *arg = CS.getArgument(0)->stripPointerCasts(); + GlobalVariable *ClassInfo = dyn_cast(arg); + if (!ClassInfo) + return false; + + std::string metaname = CD_PREFIX; + metaname += ClassInfo->getName(); + + NamedMDNode *meta = A.M.getNamedMetadata(metaname); + if (!meta) + return false; + + MDNode *node = static_cast(meta->getOperand(0)); + if (!node || node->getNumOperands() != CD_NumFields) + return false; + +// Inserting destructor calls is not implemented yet, so classes +// with destructors are ignored for now. +#if LDC_LLVM_VER >= 306 + auto hasDestructor = + mdconst::dyn_extract(node->getOperand(CD_Finalize)); +#else + Constant *hasDestructor = dyn_cast(node->getOperand(CD_Finalize)); +#endif +// We can't stack-allocate if the class has a custom deallocator +// (Custom allocators don't get turned into this runtime call, so +// those can be ignored) +#if LDC_LLVM_VER >= 306 + auto hasCustomDelete = + mdconst::dyn_extract(node->getOperand(CD_CustomDelete)); +#else + Constant *hasCustomDelete = + dyn_cast(node->getOperand(CD_CustomDelete)); +#endif + if (hasDestructor == NULL || hasCustomDelete == NULL) + return false; + + if (ConstantExpr::getOr(hasDestructor, hasCustomDelete) != + ConstantInt::getFalse(A.M.getContext())) + return false; + +#if LDC_LLVM_VER >= 306 + Ty = mdconst::dyn_extract(node->getOperand(CD_BodyType)) + ->getType(); +#else + Ty = node->getOperand(CD_BodyType)->getType(); +#endif + return A.DL.getTypeAllocSize(Ty) < SizeLimit; + } + + // The default promote() should be fine. + + AllocClassFI() : FunctionInfo(ReturnType::Pointer) {} +}; + +/// Describes runtime functions that allocate a chunk of memory with a +/// given size. +class UntypedMemoryFI : public FunctionInfo { + unsigned SizeArgNr; + Value *SizeArg; + +public: + virtual bool analyze(CallSite CS, const Analysis &A) { + if (CS.arg_size() < SizeArgNr + 1) + return false; + + SizeArg = CS.getArgument(SizeArgNr); + + // If the user explicitly disabled the limits, don't even check + // whether the allocated size fits in 32 bits. This could cause + // miscompilations for humongous allocations, but as the value + // "range" (set bits) inference algorithm is rather limited, this + // is useful for experimenting. + if (SizeLimit > 0) { + if (!isKnownLessThan(SizeArg, SizeLimit, A)) + return false; + } + + // Should be i8. + Ty = CS.getType()->getContainedType(0); + return true; + } + + virtual Value *promote(CallSite CS, IRBuilder<> &B, const Analysis &A) { + IRBuilder<> Builder = B; + // If the allocation is of constant size it's best to put it in the + // entry block, so do so if we're not already there. + // For dynamically-sized allocations it's best to avoid the overhead + // of allocating them if possible, so leave those where they are. + // While we're at it, update statistics too. + if (isa(SizeArg)) { + BasicBlock &Entry = CS.getCaller()->getEntryBlock(); + if (Builder.GetInsertBlock() != &Entry) + Builder.SetInsertPoint(&Entry, Entry.begin()); + NumGcToStack++; + } else { + NumToDynSize++; + } + + // Convert array size to 32 bits if necessary + Value *count = Builder.CreateIntCast(SizeArg, Builder.getInt32Ty(), false); + AllocaInst *alloca = + Builder.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? + + return Builder.CreateBitCast(alloca, CS.getType()); + } + + UntypedMemoryFI(unsigned sizeArgNr) + : FunctionInfo(ReturnType::Pointer), SizeArgNr(sizeArgNr) {} +}; +} //===----------------------------------------------------------------------===// // GarbageCollect2Stack Pass Implementation //===----------------------------------------------------------------------===// namespace { - /// This pass replaces GC calls with alloca's - /// - class LLVM_LIBRARY_VISIBILITY GarbageCollect2Stack : public FunctionPass { - StringMap KnownFunctions; - Module* M; +/// This pass replaces GC calls with alloca's +/// +class LLVM_LIBRARY_VISIBILITY GarbageCollect2Stack : public FunctionPass { + StringMap KnownFunctions; + Module *M; - TypeInfoFI AllocMemoryT; - ArrayFI NewArrayU; - ArrayFI NewArrayT; - AllocClassFI AllocClass; - UntypedMemoryFI AllocMemory; + TypeInfoFI AllocMemoryT; + ArrayFI NewArrayU; + ArrayFI NewArrayT; + AllocClassFI AllocClass; + UntypedMemoryFI AllocMemory; - public: - static char ID; // Pass identification - GarbageCollect2Stack(); +public: + static char ID; // Pass identification + GarbageCollect2Stack(); - bool doInitialization(Module &M) { - this->M = &M; - return false; - } + bool doInitialization(Module &M) { + this->M = &M; + return false; + } - bool runOnFunction(Function &F); + bool runOnFunction(Function &F); - virtual void getAnalysisUsage(AnalysisUsage &AU) const { + virtual void getAnalysisUsage(AnalysisUsage &AU) const { #if LDC_LLVM_VER < 307 - AU.addRequired(); + AU.addRequired(); #endif - AU.addRequired(); - AU.addPreserved(); - } - }; - char GarbageCollect2Stack::ID = 0; + AU.addRequired(); + AU.addPreserved(); + } +}; +char GarbageCollect2Stack::ID = 0; } // end anonymous namespace. static RegisterPass -X("dgc2stack", "Promote (GC'ed) heap allocations to stack"); + X("dgc2stack", "Promote (GC'ed) heap allocations to stack"); // Public interface to the pass. FunctionPass *createGarbageCollect2Stack() { @@ -409,303 +414,309 @@ FunctionPass *createGarbageCollect2Stack() { } GarbageCollect2Stack::GarbageCollect2Stack() -: FunctionPass(ID), - AllocMemoryT(ReturnType::Pointer, 0), - NewArrayU(ReturnType::Array, 0, 1, false), - NewArrayT(ReturnType::Array, 0, 1, true), - AllocMemory(0) -{ - KnownFunctions["_d_allocmemoryT"] = &AllocMemoryT; - KnownFunctions["_d_newarrayU"] = &NewArrayU; - KnownFunctions["_d_newarrayT"] = &NewArrayT; - KnownFunctions["_d_newclass"] = &AllocClass; - KnownFunctions["_d_allocmemory"] = &AllocMemory; + : FunctionPass(ID), AllocMemoryT(ReturnType::Pointer, 0), + NewArrayU(ReturnType::Array, 0, 1, false), + NewArrayT(ReturnType::Array, 0, 1, true), AllocMemory(0) { + KnownFunctions["_d_allocmemoryT"] = &AllocMemoryT; + KnownFunctions["_d_newarrayU"] = &NewArrayU; + KnownFunctions["_d_newarrayT"] = &NewArrayT; + KnownFunctions["_d_newclass"] = &AllocClass; + KnownFunctions["_d_allocmemory"] = &AllocMemory; } -static void RemoveCall(CallSite CS, const Analysis& A) { - // For an invoke instruction, we insert a branch to the normal target BB - // immediately before it. Ideally, we would find a way to not invalidate - // the dominator tree here. - if (CS.isInvoke()) { - InvokeInst* Invoke = cast(CS.getInstruction()); +static void RemoveCall(CallSite CS, const Analysis &A) { + // For an invoke instruction, we insert a branch to the normal target BB + // immediately before it. Ideally, we would find a way to not invalidate + // the dominator tree here. + if (CS.isInvoke()) { + InvokeInst *Invoke = cast(CS.getInstruction()); - BranchInst::Create(Invoke->getNormalDest(), Invoke); - Invoke->getUnwindDest()->removePredecessor(CS->getParent()); - } + BranchInst::Create(Invoke->getNormalDest(), Invoke); + Invoke->getUnwindDest()->removePredecessor(CS->getParent()); + } - // Remove the runtime call. - if (A.CGNode) - A.CGNode->removeCallEdgeFor(CS); - CS->eraseFromParent(); + // Remove the runtime call. + if (A.CGNode) + A.CGNode->removeCallEdgeFor(CS); + CS->eraseFromParent(); } -static bool isSafeToStackAllocateArray(Instruction* Alloc, DominatorTree& DT, - SmallVector& RemoveTailCallInsts -); -static bool isSafeToStackAllocate(Instruction* Alloc, Value* V, DominatorTree& DT, - SmallVector& RemoveTailCallInsts -); +static bool +isSafeToStackAllocateArray(Instruction *Alloc, DominatorTree &DT, + SmallVector &RemoveTailCallInsts); +static bool +isSafeToStackAllocate(Instruction *Alloc, Value *V, DominatorTree &DT, + SmallVector &RemoveTailCallInsts); /// runOnFunction - Top level algorithm. /// bool GarbageCollect2Stack::runOnFunction(Function &F) { - DEBUG(errs() << "\nRunning -dgc2stack on function " << F.getName() << '\n'); + DEBUG(errs() << "\nRunning -dgc2stack on function " << F.getName() << '\n'); #if LDC_LLVM_VER >= 307 - const DataLayout &DL = F.getParent()->getDataLayout(); - DominatorTree &DT = getAnalysis().getDomTree(); - CallGraphWrapperPass *CGPass = getAnalysisIfAvailable(); - CallGraph *CG = CGPass ? &CGPass->getCallGraph() : 0; + const DataLayout &DL = F.getParent()->getDataLayout(); + DominatorTree &DT = getAnalysis().getDomTree(); + CallGraphWrapperPass *CGPass = getAnalysisIfAvailable(); + CallGraph *CG = CGPass ? &CGPass->getCallGraph() : 0; #else - DataLayoutPass *DLP = getAnalysisIfAvailable(); - assert(DLP && "required DataLayoutPass is null"); - const DataLayout &DL = DLP->getDataLayout(); - DominatorTree &DT = getAnalysis().getDomTree(); - CallGraphWrapperPass *CGPass = getAnalysisIfAvailable(); - CallGraph *CG = CGPass ? &CGPass->getCallGraph() : 0; + DataLayoutPass *DLP = getAnalysisIfAvailable(); + assert(DLP && "required DataLayoutPass is null"); + const DataLayout &DL = DLP->getDataLayout(); + DominatorTree &DT = getAnalysis().getDomTree(); + CallGraphWrapperPass *CGPass = getAnalysisIfAvailable(); + CallGraph *CG = CGPass ? &CGPass->getCallGraph() : 0; #endif - CallGraphNode *CGNode = CG ? (*CG)[&F] : NULL; + CallGraphNode *CGNode = CG ? (*CG)[&F] : NULL; - Analysis A = { DL, *M, CG, CGNode }; + Analysis A = {DL, *M, CG, CGNode}; - BasicBlock& Entry = F.getEntryBlock(); + BasicBlock &Entry = F.getEntryBlock(); - IRBuilder<> AllocaBuilder(&Entry, Entry.begin()); + IRBuilder<> AllocaBuilder(&Entry, Entry.begin()); - bool Changed = false; - for (auto& BB : F) { - for (auto I = BB.begin(), E = BB.end(); I != E; ) { - // Ignore non-calls. - Instruction* Inst = I++; - CallSite CS(Inst); - if (!CS.getInstruction()) - continue; + bool Changed = false; + for (auto &BB : F) { + for (auto I = BB.begin(), E = BB.end(); I != E;) { + // Ignore non-calls. + Instruction *Inst = I++; + CallSite CS(Inst); + if (!CS.getInstruction()) + continue; - // Ignore indirect calls and calls to non-external functions. - Function *Callee = CS.getCalledFunction(); - if (Callee == 0 || !Callee->isDeclaration() || !Callee->hasExternalLinkage()) - continue; + // Ignore indirect calls and calls to non-external functions. + Function *Callee = CS.getCalledFunction(); + if (Callee == 0 || !Callee->isDeclaration() || + !Callee->hasExternalLinkage()) + continue; - // Ignore unknown calls. - auto OMI = KnownFunctions.find(Callee->getName()); - if (OMI == KnownFunctions.end()) continue; + // Ignore unknown calls. + auto OMI = KnownFunctions.find(Callee->getName()); + if (OMI == KnownFunctions.end()) + continue; - FunctionInfo* info = OMI->getValue(); + FunctionInfo *info = OMI->getValue(); - if (Inst->use_empty()) { - Changed = true; - NumDeleted++; - RemoveCall(CS, A); - continue; - } + if (Inst->use_empty()) { + Changed = true; + NumDeleted++; + RemoveCall(CS, A); + continue; + } - DEBUG(errs() << "GarbageCollect2Stack inspecting: " << *Inst); + DEBUG(errs() << "GarbageCollect2Stack inspecting: " << *Inst); - if (!info->analyze(CS, A)) - continue; + if (!info->analyze(CS, A)) + continue; - SmallVector RemoveTailCallInsts; - if (info->ReturnType == ReturnType::Array) { - if (!isSafeToStackAllocateArray(Inst, DT, RemoveTailCallInsts)) continue; - } else { - if (!isSafeToStackAllocate(Inst, Inst, DT, RemoveTailCallInsts)) continue; - } + SmallVector RemoveTailCallInsts; + if (info->ReturnType == ReturnType::Array) { + if (!isSafeToStackAllocateArray(Inst, DT, RemoveTailCallInsts)) + continue; + } else { + if (!isSafeToStackAllocate(Inst, Inst, DT, RemoveTailCallInsts)) + continue; + } - // Let's alloca this! - Changed = true; + // Let's alloca this! + Changed = true; - // First demote tail calls which use the value so there IR is never - // in an invalid state. - for (auto i : RemoveTailCallInsts) - i->setTailCall(false); + // First demote tail calls which use the value so there IR is never + // in an invalid state. + for (auto i : RemoveTailCallInsts) + i->setTailCall(false); - IRBuilder<> Builder(&BB, Inst); - Value* newVal = info->promote(CS, Builder, A); + IRBuilder<> Builder(&BB, Inst); + Value *newVal = info->promote(CS, Builder, A); - DEBUG(errs() << "Promoted to: " << *newVal); + DEBUG(errs() << "Promoted to: " << *newVal); - // Make sure the type is the same as it was before, and replace all - // uses of the runtime call with the alloca. - if (newVal->getType() != Inst->getType()) - newVal = Builder.CreateBitCast(newVal, Inst->getType()); - Inst->replaceAllUsesWith(newVal); + // Make sure the type is the same as it was before, and replace all + // uses of the runtime call with the alloca. + if (newVal->getType() != Inst->getType()) + newVal = Builder.CreateBitCast(newVal, Inst->getType()); + Inst->replaceAllUsesWith(newVal); - RemoveCall(CS, A); - } + RemoveCall(CS, A); } + } - return Changed; + return Changed; } -Type* Analysis::getTypeFor(Value* typeinfo) const { - GlobalVariable* ti_global = dyn_cast(typeinfo->stripPointerCasts()); - if (!ti_global) - return NULL; +Type *Analysis::getTypeFor(Value *typeinfo) const { + GlobalVariable *ti_global = + dyn_cast(typeinfo->stripPointerCasts()); + if (!ti_global) + return NULL; - std::string metaname = TD_PREFIX; - metaname += ti_global->getName(); + std::string metaname = TD_PREFIX; + metaname += ti_global->getName(); - NamedMDNode* meta = M.getNamedMetadata(metaname); - if (!meta) - return NULL; + NamedMDNode *meta = M.getNamedMetadata(metaname); + if (!meta) + return NULL; - MDNode* node = static_cast(meta->getOperand(0)); - if (!node) - return NULL; + MDNode *node = static_cast(meta->getOperand(0)); + if (!node) + return NULL; - if (node->getNumOperands() != TD_NumFields) - return NULL; + if (node->getNumOperands() != TD_NumFields) + return NULL; #if LDC_LLVM_VER >= 306 - Value* ti = llvm::MetadataAsValue::get(node->getContext(), node->getOperand(TD_TypeInfo)); + Value *ti = llvm::MetadataAsValue::get(node->getContext(), + node->getOperand(TD_TypeInfo)); #else - Value* ti = node->getOperand(TD_TypeInfo); + Value *ti = node->getOperand(TD_TypeInfo); #endif - if (!ti || ti->stripPointerCasts() != ti_global) - return NULL; + if (!ti || ti->stripPointerCasts() != ti_global) + return NULL; #if LDC_LLVM_VER >= 306 - return llvm::MetadataAsValue::get(node->getContext(), node->getOperand(TD_Type))->getType(); + return llvm::MetadataAsValue::get(node->getContext(), + node->getOperand(TD_Type)) + ->getType(); #else - return node->getOperand(TD_Type)->getType(); + return node->getOperand(TD_Type)->getType(); #endif } /// Returns whether Def is used by any instruction that is reachable from Alloc /// (without executing Def again). -static bool mayBeUsedAfterRealloc(Instruction* Def, Instruction* Alloc, DominatorTree& DT) { - DEBUG(errs() << "### mayBeUsedAfterRealloc()\n" << *Def << *Alloc); +static bool mayBeUsedAfterRealloc(Instruction *Def, Instruction *Alloc, + DominatorTree &DT) { + DEBUG(errs() << "### mayBeUsedAfterRealloc()\n" << *Def << *Alloc); - // If the definition isn't used it obviously won't be used after the - // allocation. - // If it does not dominate the allocation, there's no way for it to be used - // without going through Def again first, since the definition couldn't - // dominate the user either. - if (Def->use_empty() || !DT.dominates(Def, Alloc)) { - DEBUG(errs() << "### No uses or does not dominate allocation\n"); - return false; - } - - DEBUG(errs() << "### Def dominates Alloc\n"); - - BasicBlock* DefBlock = Def->getParent(); - BasicBlock* AllocBlock = Alloc->getParent(); - - // Create a set of users and one of blocks containing users. - SmallSet Users; - SmallSet UserBlocks; - for (Instruction::use_iterator UI = Def->use_begin(), UE = Def->use_end(); - UI != UE; ++UI) { - Instruction* User = cast(*UI); - DEBUG(errs() << "USER: " << *User); - BasicBlock* UserBlock = User->getParent(); - - // This dominance check is not performed if they're in the same block - // because it will just walk the instruction list to figure it out. - // We will instead do that ourselves in the first iteration (for all - // users at once). - if (AllocBlock != UserBlock && DT.dominates(AllocBlock, UserBlock)) { - // There's definitely a path from alloc to this user that does not - // go through Def, namely any path that ends up in that user. - DEBUG(errs() << "### Alloc dominates user " << *User); - return true; - } - - // Phi nodes are checked separately, so no need to enter them here. - if (!isa(User)) { - Users.insert(User); - UserBlocks.insert(UserBlock); - } - } - - // Contains first instruction of block to inspect. - typedef std::pair StartPoint; - SmallVector Worklist; - // Keeps track of successors that have been added to the work list. - SmallSet Visited; - - // Start just after the allocation. - // Note that we don't insert AllocBlock into the Visited set here so the - // start of the block will get inspected if it's reachable. - BasicBlock::iterator Start = Alloc; - ++Start; - Worklist.push_back(StartPoint(AllocBlock, Start)); - - while (!Worklist.empty()) { - StartPoint sp = Worklist.pop_back_val(); - BasicBlock* B = sp.first; - BasicBlock::iterator BBI = sp.second; - // BBI is either just after the allocation (in the first iteration) - // or just after the last phi node in B (in subsequent iterations) here. - - // This whole 'if' is just a way to avoid performing the inner 'for' - // loop when it can be determined not to be necessary, avoiding - // potentially expensive walks of the instruction list. - // It should be equivalent to just doing the loop. - if (UserBlocks.count(B)) { - if (B != DefBlock && B != AllocBlock) { - // This block does not contain the definition or the allocation, - // so any user in this block is definitely reachable without - // finding either the definition or the allocation. - DEBUG(errs() << "### Block " << B->getName() - << " contains a reachable user\n"); - return true; - } - // We need to walk the instructions in the block to see whether we - // reach a user before we reach the definition or the allocation. - for (BasicBlock::iterator E = B->end(); BBI != E; ++BBI) { - if (&*BBI == Alloc || &*BBI == Def) - break; - if (Users.count(BBI)) { - DEBUG(errs() << "### Problematic user: " << *BBI); - return true; - } - } - } else if (B == DefBlock || (B == AllocBlock && BBI != Start)) { - // There are no users in the block so the def or the allocation - // will be encountered before any users though this path. - // Skip to the next item on the worklist. - continue; - } else { - // No users and no definition or allocation after the start point, - // so just keep going. - } - - // All instructions after the starting point in this block have been - // accounted for. Look for successors to add to the work list. - TerminatorInst* Term = B->getTerminator(); - unsigned SuccCount = Term->getNumSuccessors(); - for (unsigned i = 0; i < SuccCount; i++) { - BasicBlock* Succ = Term->getSuccessor(i); - BBI = Succ->begin(); - // Check phi nodes here because we only care about the operand - // coming in from this block. - bool SeenDef = false; - while (isa(BBI)) { - if (Def == cast(BBI)->getIncomingValueForBlock(B)) { - DEBUG(errs() << "### Problematic phi user: " << *BBI); - return true; - } - SeenDef |= (Def == &*BBI); - ++BBI; - } - // If none of the phis we just looked at were the definition, we - // haven't seen this block yet, and it's dominated by the def - // (meaning paths through it could lead to users), add the block and - // the first non-phi to the worklist. - if (!SeenDef -#if LDC_LLVM_VER >= 306 - && Visited.insert(Succ).second -#else - && Visited.insert(Succ) -#endif - && DT.dominates(DefBlock, Succ)) - Worklist.push_back(StartPoint(Succ, BBI)); - } - } - // No users found in any block reachable from Alloc - // without going through the definition again. + // If the definition isn't used it obviously won't be used after the + // allocation. + // If it does not dominate the allocation, there's no way for it to be used + // without going through Def again first, since the definition couldn't + // dominate the user either. + if (Def->use_empty() || !DT.dominates(Def, Alloc)) { + DEBUG(errs() << "### No uses or does not dominate allocation\n"); return false; + } + + DEBUG(errs() << "### Def dominates Alloc\n"); + + BasicBlock *DefBlock = Def->getParent(); + BasicBlock *AllocBlock = Alloc->getParent(); + + // Create a set of users and one of blocks containing users. + SmallSet Users; + SmallSet UserBlocks; + for (Instruction::use_iterator UI = Def->use_begin(), UE = Def->use_end(); + UI != UE; ++UI) { + Instruction *User = cast(*UI); + DEBUG(errs() << "USER: " << *User); + BasicBlock *UserBlock = User->getParent(); + + // This dominance check is not performed if they're in the same block + // because it will just walk the instruction list to figure it out. + // We will instead do that ourselves in the first iteration (for all + // users at once). + if (AllocBlock != UserBlock && DT.dominates(AllocBlock, UserBlock)) { + // There's definitely a path from alloc to this user that does not + // go through Def, namely any path that ends up in that user. + DEBUG(errs() << "### Alloc dominates user " << *User); + return true; + } + + // Phi nodes are checked separately, so no need to enter them here. + if (!isa(User)) { + Users.insert(User); + UserBlocks.insert(UserBlock); + } + } + + // Contains first instruction of block to inspect. + typedef std::pair StartPoint; + SmallVector Worklist; + // Keeps track of successors that have been added to the work list. + SmallSet Visited; + + // Start just after the allocation. + // Note that we don't insert AllocBlock into the Visited set here so the + // start of the block will get inspected if it's reachable. + BasicBlock::iterator Start = Alloc; + ++Start; + Worklist.push_back(StartPoint(AllocBlock, Start)); + + while (!Worklist.empty()) { + StartPoint sp = Worklist.pop_back_val(); + BasicBlock *B = sp.first; + BasicBlock::iterator BBI = sp.second; + // BBI is either just after the allocation (in the first iteration) + // or just after the last phi node in B (in subsequent iterations) here. + + // This whole 'if' is just a way to avoid performing the inner 'for' + // loop when it can be determined not to be necessary, avoiding + // potentially expensive walks of the instruction list. + // It should be equivalent to just doing the loop. + if (UserBlocks.count(B)) { + if (B != DefBlock && B != AllocBlock) { + // This block does not contain the definition or the allocation, + // so any user in this block is definitely reachable without + // finding either the definition or the allocation. + DEBUG(errs() << "### Block " << B->getName() + << " contains a reachable user\n"); + return true; + } + // We need to walk the instructions in the block to see whether we + // reach a user before we reach the definition or the allocation. + for (BasicBlock::iterator E = B->end(); BBI != E; ++BBI) { + if (&*BBI == Alloc || &*BBI == Def) + break; + if (Users.count(BBI)) { + DEBUG(errs() << "### Problematic user: " << *BBI); + return true; + } + } + } else if (B == DefBlock || (B == AllocBlock && BBI != Start)) { + // There are no users in the block so the def or the allocation + // will be encountered before any users though this path. + // Skip to the next item on the worklist. + continue; + } else { + // No users and no definition or allocation after the start point, + // so just keep going. + } + + // All instructions after the starting point in this block have been + // accounted for. Look for successors to add to the work list. + TerminatorInst *Term = B->getTerminator(); + unsigned SuccCount = Term->getNumSuccessors(); + for (unsigned i = 0; i < SuccCount; i++) { + BasicBlock *Succ = Term->getSuccessor(i); + BBI = Succ->begin(); + // Check phi nodes here because we only care about the operand + // coming in from this block. + bool SeenDef = false; + while (isa(BBI)) { + if (Def == cast(BBI)->getIncomingValueForBlock(B)) { + DEBUG(errs() << "### Problematic phi user: " << *BBI); + return true; + } + SeenDef |= (Def == &*BBI); + ++BBI; + } + // If none of the phis we just looked at were the definition, we + // haven't seen this block yet, and it's dominated by the def + // (meaning paths through it could lead to users), add the block and + // the first non-phi to the worklist. + if (!SeenDef +#if LDC_LLVM_VER >= 306 + && Visited.insert(Succ).second +#else + && Visited.insert(Succ) +#endif + && DT.dominates(DefBlock, Succ)) + Worklist.push_back(StartPoint(Succ, BBI)); + } + } + // No users found in any block reachable from Alloc + // without going through the definition again. + return false; } /// Returns true if the GC call passed in is safe to turn into a stack @@ -713,47 +724,47 @@ static bool mayBeUsedAfterRealloc(Instruction* Def, Instruction* Alloc, Dominato /// /// This handles GC calls returning a D array instead of a raw pointer, /// see isSafeToStackAllocate() for details. -bool isSafeToStackAllocateArray(Instruction* Alloc, DominatorTree& DT, - SmallVector& RemoveTailCallInsts -) { - assert(Alloc->getType()->isStructTy() && "Allocated array is not a struct?"); - Value* V = Alloc; +bool isSafeToStackAllocateArray( + Instruction *Alloc, DominatorTree &DT, + SmallVector &RemoveTailCallInsts) { + assert(Alloc->getType()->isStructTy() && "Allocated array is not a struct?"); + Value *V = Alloc; - for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); - UI != UE; ++UI) { - Instruction *User = cast(*UI); + for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); UI != UE; + ++UI) { + Instruction *User = cast(*UI); - switch (User->getOpcode()) { - case Instruction::ExtractValue: { - ExtractValueInst *EVI = cast(User); + switch (User->getOpcode()) { + case Instruction::ExtractValue: { + ExtractValueInst *EVI = cast(User); - assert(EVI->getAggregateOperand() == V); - assert(EVI->getNumIndices() == 1); + assert(EVI->getAggregateOperand() == V); + assert(EVI->getNumIndices() == 1); - unsigned idx = EVI->getIndices()[0]; - if (idx == 0) { - // This extract the length argument, irrelevant for our analysis. - assert(EVI->getType()->isIntegerTy() && "First array field not length?"); - } else { - assert(idx == 1 && "Invalid array struct access."); - if (!isSafeToStackAllocate(Alloc, EVI, DT, RemoveTailCallInsts)) - return false; - } - break; - } - default: - // We are super conservative here, the only thing we want to be able to - // handle at this point is extracting len/ptr. More extensive analysis - // could be added later. - return false; - } + unsigned idx = EVI->getIndices()[0]; + if (idx == 0) { + // This extract the length argument, irrelevant for our analysis. + assert(EVI->getType()->isIntegerTy() && + "First array field not length?"); + } else { + assert(idx == 1 && "Invalid array struct access."); + if (!isSafeToStackAllocate(Alloc, EVI, DT, RemoveTailCallInsts)) + return false; + } + break; } + default: + // We are super conservative here, the only thing we want to be able to + // handle at this point is extracting len/ptr. More extensive analysis + // could be added later. + return false; + } + } - // All uses examined - memory not captured. - return true; + // All uses examined - memory not captured. + return true; } - /// Returns true if the GC call passed in is safe to turn /// into a stack allocation. This requires that the return value does not /// escape from the function and no derived pointers are live at the call site @@ -775,16 +786,15 @@ bool isSafeToStackAllocateArray(Instruction* Alloc, DominatorTree& DT, /// the attribute has to be removed before promoting the memory to the /// stack. The affected instructions are added to RemoveTailCallInsts. If /// the function returns false, these entries are meaningless. -bool isSafeToStackAllocate(Instruction* Alloc, Value* V, DominatorTree& DT, - SmallVector& RemoveTailCallInsts -) { +bool isSafeToStackAllocate(Instruction *Alloc, Value *V, DominatorTree &DT, + SmallVector &RemoveTailCallInsts) { assert(isa(V->getType()) && "Allocated value is not a pointer?"); - SmallVector Worklist; - SmallSet Visited; + SmallVector Worklist; + SmallSet Visited; - for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); - UI != UE; ++UI) { + for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); UI != UE; + ++UI) { Use *U = &(*UI); Visited.insert(U); Worklist.push_back(U); @@ -822,7 +832,7 @@ bool isSafeToStackAllocate(Instruction* Alloc, Value* V, DominatorTree& DT, } if (CS.isCall()) { - CallInst* CI = cast(I); + CallInst *CI = cast(I); if (CI->isTailCall()) { RemoveTailCallInsts.push_back(CI); } diff --git a/gen/passes/Passes.h b/gen/passes/Passes.h index 4a113a596a..d00a502927 100644 --- a/gen/passes/Passes.h +++ b/gen/passes/Passes.h @@ -16,15 +16,15 @@ #include "gen/metadata.h" namespace llvm { - class FunctionPass; - class ModulePass; +class FunctionPass; +class ModulePass; } // Performs simplifications on runtime calls. -llvm::FunctionPass* createSimplifyDRuntimeCalls(); +llvm::FunctionPass *createSimplifyDRuntimeCalls(); -llvm::FunctionPass* createGarbageCollect2Stack(); +llvm::FunctionPass *createGarbageCollect2Stack(); -llvm::ModulePass* createStripExternalsPass(); +llvm::ModulePass *createStripExternalsPass(); #endif diff --git a/gen/passes/SimplifyDRuntimeCalls.cpp b/gen/passes/SimplifyDRuntimeCalls.cpp index b07d97ec7f..8d70437ea6 100644 --- a/gen/passes/SimplifyDRuntimeCalls.cpp +++ b/gen/passes/SimplifyDRuntimeCalls.cpp @@ -52,43 +52,45 @@ STATISTIC(NumDeleted, "Number of runtime calls deleted"); /// This class is the abstract base class for the set of optimizations that /// corresponds to one library call. namespace { - class LLVM_LIBRARY_VISIBILITY LibCallOptimization { - protected: - Function *Caller; - bool *Changed; - const DataLayout *DL; - AliasAnalysis *AA; - LLVMContext *Context; +class LLVM_LIBRARY_VISIBILITY LibCallOptimization { +protected: + Function *Caller; + bool *Changed; + const DataLayout *DL; + AliasAnalysis *AA; + LLVMContext *Context; - /// CastToCStr - Return V if it is an i8*, otherwise cast it to i8*. - Value *CastToCStr(Value *V, IRBuilder<> &B); + /// CastToCStr - Return V if it is an i8*, otherwise cast it to i8*. + Value *CastToCStr(Value *V, IRBuilder<> &B); - /// EmitMemCpy - Emit a call to the memcpy function to the builder. This - /// always expects that the size has type 'intptr_t' and Dst/Src are pointers. - Value *EmitMemCpy(Value *Dst, Value *Src, Value *Len, - unsigned Align, IRBuilder<> &B); - public: - LibCallOptimization() { } - virtual ~LibCallOptimization() {} + /// EmitMemCpy - Emit a call to the memcpy function to the builder. This + /// always expects that the size has type 'intptr_t' and Dst/Src are pointers. + Value *EmitMemCpy(Value *Dst, Value *Src, Value *Len, unsigned Align, + IRBuilder<> &B); - /// CallOptimizer - This pure virtual method is implemented by base classes to - /// do various optimizations. If this returns null then no transformation was - /// performed. If it returns CI, then it transformed the call and CI is to be - /// deleted. If it returns something else, replace CI with the new value and - /// delete CI. - virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B)=0; +public: + LibCallOptimization() {} + virtual ~LibCallOptimization() {} - Value *OptimizeCall(CallInst *CI, bool& Changed, const DataLayout *DL, - AliasAnalysis& AA, IRBuilder<> &B) { - Caller = CI->getParent()->getParent(); - this->Changed = &Changed; - this->DL = DL; - this->AA = &AA; - if (CI->getCalledFunction()) - Context = &CI->getCalledFunction()->getContext(); - return CallOptimizer(CI->getCalledFunction(), CI, B); - } - }; + /// CallOptimizer - This pure virtual method is implemented by base classes to + /// do various optimizations. If this returns null then no transformation was + /// performed. If it returns CI, then it transformed the call and CI is to be + /// deleted. If it returns something else, replace CI with the new value and + /// delete CI. + virtual Value *CallOptimizer(Function *Callee, CallInst *CI, + IRBuilder<> &B) = 0; + + Value *OptimizeCall(CallInst *CI, bool &Changed, const DataLayout *DL, + AliasAnalysis &AA, IRBuilder<> &B) { + Caller = CI->getParent()->getParent(); + this->Changed = &Changed; + this->DL = DL; + this->AA = &AA; + if (CI->getCalledFunction()) + Context = &CI->getCalledFunction()->getContext(); + return CallOptimizer(CI->getCalledFunction(), CI, B); + } +}; } // End anonymous namespace. /// CastToCStr - Return V if it is an i8*, otherwise cast it to i8*. @@ -100,7 +102,8 @@ Value *LibCallOptimization::CastToCStr(Value *V, IRBuilder<> &B) { /// expects that the size has type 'intptr_t' and Dst/Src are pointers. Value *LibCallOptimization::EmitMemCpy(Value *Dst, Value *Src, Value *Len, unsigned Align, IRBuilder<> &B) { - return B.CreateMemCpy(CastToCStr(Dst, B), CastToCStr(Src, B), Len, Align, false); + return B.CreateMemCpy(CastToCStr(Dst, B), CastToCStr(Src, B), Len, Align, + false); } //===----------------------------------------------------------------------===// @@ -113,155 +116,158 @@ namespace { /// ArraySetLengthOpt - remove libcall for arr.length = N if N <= arr.length struct LLVM_LIBRARY_VISIBILITY ArraySetLengthOpt : public LibCallOptimization { - virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) { - // Verify we have a reasonable prototype for _d_arraysetlength[i]T - const FunctionType *FT = Callee->getFunctionType(); - if (Callee->arg_size() != 4 || !isa(FT->getReturnType()) || - !isa(FT->getParamType(1)) || - FT->getParamType(1) != FT->getParamType(2) || - FT->getParamType(3) != FT->getReturnType()) - return 0; + virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) { + // Verify we have a reasonable prototype for _d_arraysetlength[i]T + const FunctionType *FT = Callee->getFunctionType(); + if (Callee->arg_size() != 4 || !isa(FT->getReturnType()) || + !isa(FT->getParamType(1)) || + FT->getParamType(1) != FT->getParamType(2) || + FT->getParamType(3) != FT->getReturnType()) + return 0; - // Whether or not this allocates is irrelevant if the result isn't used. - // Just delete if that's the case. - if (CI->use_empty()) - return CI; + // Whether or not this allocates is irrelevant if the result isn't used. + // Just delete if that's the case. + if (CI->use_empty()) + return CI; - Value* NewLen = CI->getOperand(1); - if (Constant* NewCst = dyn_cast(NewLen)) { - Value* Data = CI->getOperand(3); + Value *NewLen = CI->getOperand(1); + if (Constant *NewCst = dyn_cast(NewLen)) { + Value *Data = CI->getOperand(3); - // For now, we just catch the simplest of cases. - // - // TODO: Implement a more general way to compare old and new - // lengths, to catch cases like "arr.length = arr.length - 1;" - // (But beware of unsigned overflow! For example, we can't - // safely transform that example if arr.length may be 0) + // For now, we just catch the simplest of cases. + // + // TODO: Implement a more general way to compare old and new + // lengths, to catch cases like "arr.length = arr.length - 1;" + // (But beware of unsigned overflow! For example, we can't + // safely transform that example if arr.length may be 0) - // Setting length to 0 never reallocates, so replace by data argument - if (NewCst->isNullValue()) - return Data; + // Setting length to 0 never reallocates, so replace by data argument + if (NewCst->isNullValue()) + return Data; - // If both lengths are constant integers, see if NewLen <= OldLen - Value* OldLen = CI->getOperand(2); - if (ConstantInt* OldInt = dyn_cast(OldLen)) - if (ConstantInt* NewInt = dyn_cast(NewCst)) - if (NewInt->getValue().ule(OldInt->getValue())) - return Data; - } - return 0; + // If both lengths are constant integers, see if NewLen <= OldLen + Value *OldLen = CI->getOperand(2); + if (ConstantInt *OldInt = dyn_cast(OldLen)) + if (ConstantInt *NewInt = dyn_cast(NewCst)) + if (NewInt->getValue().ule(OldInt->getValue())) + return Data; } + return 0; + } }; /// ArrayCastLenOpt - remove libcall for cast(T[]) arr if it's safe to do so. struct LLVM_LIBRARY_VISIBILITY ArrayCastLenOpt : public LibCallOptimization { - virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) { - // Verify we have a reasonable prototype for _d_array_cast_len - const FunctionType *FT = Callee->getFunctionType(); - const Type* RetTy = FT->getReturnType(); - if (Callee->arg_size() != 3 || !isa(RetTy) || - FT->getParamType(0) != RetTy || FT->getParamType(1) != RetTy || - FT->getParamType(2) != RetTy) + virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) { + // Verify we have a reasonable prototype for _d_array_cast_len + const FunctionType *FT = Callee->getFunctionType(); + const Type *RetTy = FT->getReturnType(); + if (Callee->arg_size() != 3 || !isa(RetTy) || + FT->getParamType(0) != RetTy || FT->getParamType(1) != RetTy || + FT->getParamType(2) != RetTy) + return 0; + + Value *OldLen = CI->getOperand(0); + Value *OldSize = CI->getOperand(1); + Value *NewSize = CI->getOperand(2); + + // If the old length was zero, always return zero. + if (Constant *LenCst = dyn_cast(OldLen)) + if (LenCst->isNullValue()) + return OldLen; + + // Equal sizes are much faster to check for, so do so now. + if (OldSize == NewSize) + return OldLen; + + // If both sizes are constant integers, see if OldSize is a multiple of + // NewSize + if (ConstantInt *OldInt = dyn_cast(OldSize)) + if (ConstantInt *NewInt = dyn_cast(NewSize)) { + // Don't crash on NewSize == 0, even though it shouldn't happen. + if (NewInt->isNullValue()) return 0; - Value* OldLen = CI->getOperand(0); - Value* OldSize = CI->getOperand(1); - Value* NewSize = CI->getOperand(2); - - // If the old length was zero, always return zero. - if (Constant* LenCst = dyn_cast(OldLen)) - if (LenCst->isNullValue()) - return OldLen; - - // Equal sizes are much faster to check for, so do so now. - if (OldSize == NewSize) - return OldLen; - - // If both sizes are constant integers, see if OldSize is a multiple of NewSize - if (ConstantInt* OldInt = dyn_cast(OldSize)) - if (ConstantInt* NewInt = dyn_cast(NewSize)) { - // Don't crash on NewSize == 0, even though it shouldn't happen. - if (NewInt->isNullValue()) - return 0; - - APInt Quot, Rem; - APInt::udivrem(OldInt->getValue(), NewInt->getValue(), Quot, Rem); - if (Rem == 0) - return B.CreateMul(OldLen, ConstantInt::get(*Context, Quot)); - } - return 0; - } + APInt Quot, Rem; + APInt::udivrem(OldInt->getValue(), NewInt->getValue(), Quot, Rem); + if (Rem == 0) + return B.CreateMul(OldLen, ConstantInt::get(*Context, Quot)); + } + return 0; + } }; /// AllocationOpt - Common optimizations for various GC allocations. struct LLVM_LIBRARY_VISIBILITY AllocationOpt : public LibCallOptimization { - virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) { - // Allocations are never equal to constants, so remove any equality - // comparisons to constants. (Most importantly comparisons to null at - // the start of inlined member functions) - for (CallInst::use_iterator I = CI->use_begin(), E = CI->use_end() ; I != E;) { - Instruction* User = cast(*I++); + virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) { + // Allocations are never equal to constants, so remove any equality + // comparisons to constants. (Most importantly comparisons to null at + // the start of inlined member functions) + for (CallInst::use_iterator I = CI->use_begin(), E = CI->use_end(); + I != E;) { + Instruction *User = cast(*I++); - if (ICmpInst* Cmp = dyn_cast(User)) { - if (!Cmp->isEquality()) - continue; - Constant* C = 0; - if ((C = dyn_cast(Cmp->getOperand(0))) - || (C = dyn_cast(Cmp->getOperand(1)))) { - Value* Result = ConstantInt::get(B.getInt1Ty(), !Cmp->isTrueWhenEqual()); - Cmp->replaceAllUsesWith(Result); - // Don't delete the comparison because there may be an - // iterator to it. Instead, set the operands to constants - // and let dead code elimination clean it up later. - // (It doesn't matter that this changes the value of the - // icmp because it's not used anymore anyway) - Cmp->setOperand(0, C); - Cmp->setOperand(1, C); - *Changed = true; - } - } + if (ICmpInst *Cmp = dyn_cast(User)) { + if (!Cmp->isEquality()) + continue; + Constant *C = 0; + if ((C = dyn_cast(Cmp->getOperand(0))) || + (C = dyn_cast(Cmp->getOperand(1)))) { + Value *Result = + ConstantInt::get(B.getInt1Ty(), !Cmp->isTrueWhenEqual()); + Cmp->replaceAllUsesWith(Result); + // Don't delete the comparison because there may be an + // iterator to it. Instead, set the operands to constants + // and let dead code elimination clean it up later. + // (It doesn't matter that this changes the value of the + // icmp because it's not used anymore anyway) + Cmp->setOperand(0, C); + Cmp->setOperand(1, C); + *Changed = true; } - - // If it's not used (anymore), pre-emptively GC it. - if (CI->use_empty()) - return CI; - return 0; + } } + + // If it's not used (anymore), pre-emptively GC it. + if (CI->use_empty()) + return CI; + return 0; + } }; /// ArraySliceCopyOpt - Turn slice copies into llvm.memcpy when safe struct LLVM_LIBRARY_VISIBILITY ArraySliceCopyOpt : public LibCallOptimization { - virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) { - // Verify we have a reasonable prototype for _d_array_slice_copy - const FunctionType *FT = Callee->getFunctionType(); - const Type* VoidPtrTy = PointerType::getUnqual(B.getInt8Ty()); - if (Callee->arg_size() != 4 || FT->getReturnType() != B.getVoidTy() || - FT->getParamType(0) != VoidPtrTy || - !isa(FT->getParamType(1)) || - FT->getParamType(2) != VoidPtrTy || - FT->getParamType(3) != FT->getParamType(1)) - return 0; + virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) { + // Verify we have a reasonable prototype for _d_array_slice_copy + const FunctionType *FT = Callee->getFunctionType(); + const Type *VoidPtrTy = PointerType::getUnqual(B.getInt8Ty()); + if (Callee->arg_size() != 4 || FT->getReturnType() != B.getVoidTy() || + FT->getParamType(0) != VoidPtrTy || + !isa(FT->getParamType(1)) || + FT->getParamType(2) != VoidPtrTy || + FT->getParamType(3) != FT->getParamType(1)) + return 0; - Value* Size = CI->getOperand(1); + Value *Size = CI->getOperand(1); - // Check the lengths match - if (CI->getOperand(3) != Size) - return 0; + // Check the lengths match + if (CI->getOperand(3) != Size) + return 0; - // Assume unknown size unless we have constant size (that fits in an uint) - unsigned Sz = ~0U; - if (ConstantInt* Int = dyn_cast(Size)) - if (Int->getValue().isIntN(32)) - Sz = Int->getValue().getZExtValue(); + // Assume unknown size unless we have constant size (that fits in an uint) + unsigned Sz = ~0U; + if (ConstantInt *Int = dyn_cast(Size)) + if (Int->getValue().isIntN(32)) + Sz = Int->getValue().getZExtValue(); - // Check if the pointers may alias - if (AA->alias(CI->getOperand(0), Sz, CI->getOperand(2), Sz)) - return 0; + // Check if the pointers may alias + if (AA->alias(CI->getOperand(0), Sz, CI->getOperand(2), Sz)) + return 0; - // Equal length and the pointers definitely don't alias, so it's safe to - // replace the call with memcpy - return EmitMemCpy(CI->getOperand(0), CI->getOperand(2), Size, 1, B); - } + // Equal length and the pointers definitely don't alias, so it's safe to + // replace the call with memcpy + return EmitMemCpy(CI->getOperand(0), CI->getOperand(2), Size, 1, B); + } }; // TODO: More optimizations! :) @@ -273,42 +279,42 @@ struct LLVM_LIBRARY_VISIBILITY ArraySliceCopyOpt : public LibCallOptimization { //===----------------------------------------------------------------------===// namespace { - /// This pass optimizes library functions from the D runtime as used by LDC. - /// - class LLVM_LIBRARY_VISIBILITY SimplifyDRuntimeCalls : public FunctionPass { - StringMap Optimizations; +/// This pass optimizes library functions from the D runtime as used by LDC. +/// +class LLVM_LIBRARY_VISIBILITY SimplifyDRuntimeCalls : public FunctionPass { + StringMap Optimizations; - // Array operations - ArraySetLengthOpt ArraySetLength; - ArrayCastLenOpt ArrayCastLen; - ArraySliceCopyOpt ArraySliceCopy; + // Array operations + ArraySetLengthOpt ArraySetLength; + ArrayCastLenOpt ArrayCastLen; + ArraySliceCopyOpt ArraySliceCopy; - // GC allocations - AllocationOpt Allocation; + // GC allocations + AllocationOpt Allocation; - public: - static char ID; // Pass identification - SimplifyDRuntimeCalls() : FunctionPass(ID) {} +public: + static char ID; // Pass identification + SimplifyDRuntimeCalls() : FunctionPass(ID) {} - void InitOptimizations(); - bool runOnFunction(Function &F); + void InitOptimizations(); + bool runOnFunction(Function &F); - bool runOnce(Function &F, const DataLayout *DL, AliasAnalysisPass& AA); + bool runOnce(Function &F, const DataLayout *DL, AliasAnalysisPass &AA); - virtual void getAnalysisUsage(AnalysisUsage &AU) const { + virtual void getAnalysisUsage(AnalysisUsage &AU) const { #if LDC_LLVM_VER >= 307 - // The DataLayoutPass is removed. +// The DataLayoutPass is removed. #else - AU.addRequired(); + AU.addRequired(); #endif - AU.addRequired(); - } - }; - char SimplifyDRuntimeCalls::ID = 0; + AU.addRequired(); + } +}; +char SimplifyDRuntimeCalls::ID = 0; } // end anonymous namespace. -static RegisterPass -X("simplify-drtcalls", "Simplify calls to D runtime"); +static RegisterPass X("simplify-drtcalls", + "Simplify calls to D runtime"); // Public interface to the pass. FunctionPass *createSimplifyDRuntimeCalls() { @@ -318,125 +324,130 @@ FunctionPass *createSimplifyDRuntimeCalls() { /// Optimizations - Populate the Optimizations map with all the optimizations /// we know. void SimplifyDRuntimeCalls::InitOptimizations() { - // Some array-related optimizations - Optimizations["_d_arraysetlengthT"] = &ArraySetLength; - Optimizations["_d_arraysetlengthiT"] = &ArraySetLength; - Optimizations["_d_array_cast_len"] = &ArrayCastLen; - Optimizations["_d_array_slice_copy"] = &ArraySliceCopy; + // Some array-related optimizations + Optimizations["_d_arraysetlengthT"] = &ArraySetLength; + Optimizations["_d_arraysetlengthiT"] = &ArraySetLength; + Optimizations["_d_array_cast_len"] = &ArrayCastLen; + Optimizations["_d_array_slice_copy"] = &ArraySliceCopy; - /* Delete calls to runtime functions which aren't needed if their result is - * unused. That comes down to functions that don't do anything but - * GC-allocate and initialize some memory. - * We don't need to do this for functions which are marked 'readnone' or - * 'readonly', since LLVM doesn't need our help figuring out when those can - * be deleted. - * (We can't mark allocating calls as readonly/readnone because they don't - * return the same pointer every time when called with the same arguments) - */ - Optimizations["_d_allocmemoryT"] = &Allocation; - Optimizations["_d_newarrayT"] = &Allocation; - Optimizations["_d_newarrayiT"] = &Allocation; - Optimizations["_d_newarrayU"] = &Allocation; - Optimizations["_d_newarraymT"] = &Allocation; - Optimizations["_d_newarraymiT"] = &Allocation; - Optimizations["_d_newarraymvT"] = &Allocation; - Optimizations["_d_newclass"] = &Allocation; + /* Delete calls to runtime functions which aren't needed if their result is + * unused. That comes down to functions that don't do anything but + * GC-allocate and initialize some memory. + * We don't need to do this for functions which are marked 'readnone' or + * 'readonly', since LLVM doesn't need our help figuring out when those can + * be deleted. + * (We can't mark allocating calls as readonly/readnone because they don't + * return the same pointer every time when called with the same arguments) + */ + Optimizations["_d_allocmemoryT"] = &Allocation; + Optimizations["_d_newarrayT"] = &Allocation; + Optimizations["_d_newarrayiT"] = &Allocation; + Optimizations["_d_newarrayU"] = &Allocation; + Optimizations["_d_newarraymT"] = &Allocation; + Optimizations["_d_newarraymiT"] = &Allocation; + Optimizations["_d_newarraymvT"] = &Allocation; + Optimizations["_d_newclass"] = &Allocation; } - /// runOnFunction - Top level algorithm. /// bool SimplifyDRuntimeCalls::runOnFunction(Function &F) { - if (Optimizations.empty()) - InitOptimizations(); + if (Optimizations.empty()) + InitOptimizations(); #if LDC_LLVM_VER >= 307 - const DataLayout *DL = &F.getParent()->getDataLayout(); + const DataLayout *DL = &F.getParent()->getDataLayout(); #else - DataLayoutPass *DLP = getAnalysisIfAvailable(); - const DataLayout *DL = DLP ? &DLP->getDataLayout() : 0; + DataLayoutPass *DLP = getAnalysisIfAvailable(); + const DataLayout *DL = DLP ? &DLP->getDataLayout() : 0; #endif - AliasAnalysisPass &AA = getAnalysis(); + AliasAnalysisPass &AA = getAnalysis(); - // Iterate to catch opportunities opened up by other optimizations, - // such as calls that are only used as arguments to unused calls: - // When the second call gets deleted the first call will become unused, but - // without iteration we wouldn't notice if we inspected the first call - // before the second one. - bool EverChanged = false; - bool Changed; - do { - Changed = runOnce(F, DL, AA); - EverChanged |= Changed; - } while (Changed); + // Iterate to catch opportunities opened up by other optimizations, + // such as calls that are only used as arguments to unused calls: + // When the second call gets deleted the first call will become unused, but + // without iteration we wouldn't notice if we inspected the first call + // before the second one. + bool EverChanged = false; + bool Changed; + do { + Changed = runOnce(F, DL, AA); + EverChanged |= Changed; + } while (Changed); - return EverChanged; + return EverChanged; } -bool SimplifyDRuntimeCalls::runOnce(Function &F, const DataLayout *DL, AliasAnalysisPass& AAP) { - IRBuilder<> Builder(F.getContext()); +bool SimplifyDRuntimeCalls::runOnce(Function &F, const DataLayout *DL, + AliasAnalysisPass &AAP) { + IRBuilder<> Builder(F.getContext()); - bool Changed = false; - for (auto& BB : F) { - for (auto I = BB.begin(); I != BB.end(); ) { - // Ignore non-calls. - CallInst *CI = dyn_cast(I++); - if (!CI) continue; + bool Changed = false; + for (auto &BB : F) { + for (auto I = BB.begin(); I != BB.end();) { + // Ignore non-calls. + CallInst *CI = dyn_cast(I++); + if (!CI) + continue; - // Ignore indirect calls and calls to non-external functions. - Function *Callee = CI->getCalledFunction(); - if (Callee == nullptr || !Callee->isDeclaration() || !Callee->hasExternalLinkage()) - continue; + // Ignore indirect calls and calls to non-external functions. + Function *Callee = CI->getCalledFunction(); + if (Callee == nullptr || !Callee->isDeclaration() || + !Callee->hasExternalLinkage()) + continue; - // Ignore unknown calls. - auto OMI = Optimizations.find(Callee->getName()); - if (OMI == Optimizations.end()) continue; + // Ignore unknown calls. + auto OMI = Optimizations.find(Callee->getName()); + if (OMI == Optimizations.end()) + continue; - DEBUG(errs() << "SimplifyDRuntimeCalls inspecting: " << *CI); + DEBUG(errs() << "SimplifyDRuntimeCalls inspecting: " << *CI); - // Set the builder to the instruction after the call. - Builder.SetInsertPoint(&BB, I); + // Set the builder to the instruction after the call. + Builder.SetInsertPoint(&BB, I); - // Try to optimize this call. +// Try to optimize this call. #if LDC_LLVM_VER >= 308 - AliasAnalysis& AA = AAP.getAAResults(); + AliasAnalysis &AA = AAP.getAAResults(); #else - AliasAnalysis& AA = AAP; + AliasAnalysis &AA = AAP; #endif - Value *Result = OMI->second->OptimizeCall(CI, Changed, DL, AA, Builder); - if (Result == 0) continue; + Value *Result = OMI->second->OptimizeCall(CI, Changed, DL, AA, Builder); + if (Result == 0) + continue; - DEBUG(errs() << "SimplifyDRuntimeCalls simplified: " << *CI; - errs() << " into: " << *Result << "\n"); + DEBUG(errs() << "SimplifyDRuntimeCalls simplified: " << *CI; + errs() << " into: " << *Result << "\n"); - // Something changed! - Changed = true; + // Something changed! + Changed = true; - if (Result == CI) { - assert(CI->use_empty()); - ++NumDeleted; + if (Result == CI) { + assert(CI->use_empty()); + ++NumDeleted; #if LDC_LLVM_VER < 308 - AA.deleteValue(CI); + AA.deleteValue(CI); #endif - } else { - ++NumSimplified; + } else { + ++NumSimplified; #if LDC_LLVM_VER < 308 - AA.replaceWithNewValue(CI, Result); + AA.replaceWithNewValue(CI, Result); #endif - if (!CI->use_empty()) - CI->replaceAllUsesWith(Result); + if (!CI->use_empty()) + CI->replaceAllUsesWith(Result); - if (!Result->hasName()) - Result->takeName(CI); - } + if (!Result->hasName()) + Result->takeName(CI); + } - // Inspect the instruction after the call (which was potentially just - // added) next. - I = CI; ++I; + // Inspect the instruction after the call (which was potentially just + // added) next. + I = CI; + ++I; - CI->eraseFromParent(); - } + CI->eraseFromParent(); } - return Changed; + } + return Changed; } diff --git a/gen/passes/StripExternals.cpp b/gen/passes/StripExternals.cpp index 7808af2414..91a49ae34c 100644 --- a/gen/passes/StripExternals.cpp +++ b/gen/passes/StripExternals.cpp @@ -31,28 +31,29 @@ STATISTIC(NumFunctions, "Number of function bodies removed"); STATISTIC(NumVariables, "Number of global initializers removed"); namespace { - struct LLVM_LIBRARY_VISIBILITY StripExternals : public ModulePass { - static char ID; // Pass identification, replacement for typeid - StripExternals() : ModulePass(ID) {} +struct LLVM_LIBRARY_VISIBILITY StripExternals : public ModulePass { + static char ID; // Pass identification, replacement for typeid + StripExternals() : ModulePass(ID) {} - // run - Do the StripExternals pass on the specified module. - // - bool runOnModule(Module &M); - }; + // run - Do the StripExternals pass on the specified module. + // + bool runOnModule(Module &M); +}; } char StripExternals::ID = 0; static RegisterPass -X("strip-externals", "Strip available_externally bodies and initializers"); + X("strip-externals", "Strip available_externally bodies and initializers"); ModulePass *createStripExternalsPass() { return new StripExternals(); } bool StripExternals::runOnModule(Module &M) { bool Changed = false; - for (auto I = M.begin(); I != M.end(); ) { + for (auto I = M.begin(); I != M.end();) { if (I->hasAvailableExternallyLinkage()) { - assert(!I->isDeclaration()&&"Declarations can't be available_externally"); + assert(!I->isDeclaration() && + "Declarations can't be available_externally"); Changed = true; ++NumFunctions; if (I->use_empty()) { @@ -69,10 +70,10 @@ bool StripExternals::runOnModule(Module &M) { ++I; } - for (Module::global_iterator I = M.global_begin(); - I != M.global_end(); ) { + for (Module::global_iterator I = M.global_begin(); I != M.global_end();) { if (I->hasAvailableExternallyLinkage()) { - assert(!I->isDeclaration()&&"Declarations can't be available_externally"); + assert(!I->isDeclaration() && + "Declarations can't be available_externally"); Changed = true; ++NumVariables; if (I->use_empty()) { diff --git a/gen/pragma.cpp b/gen/pragma.cpp index fea15ce990..579355f5cd 100644 --- a/gen/pragma.cpp +++ b/gen/pragma.cpp @@ -17,581 +17,496 @@ #include "template.h" #include "llvm/Support/CommandLine.h" -static bool parseStringExp(Expression* e, std::string& res) -{ - StringExp *s = NULL; +static bool parseStringExp(Expression *e, std::string &res) { + StringExp *s = NULL; - e = e->optimize(WANTvalue); - if (e->op == TOKstring && (s = static_cast(e))) - { - char* str = static_cast(s->string); - res = str; - return true; - } - return false; + e = e->optimize(WANTvalue); + if (e->op == TOKstring && (s = static_cast(e))) { + char *str = static_cast(s->string); + res = str; + return true; + } + return false; } -static bool parseIntExp(Expression* e, dinteger_t& res) -{ - IntegerExp *i = NULL; +static bool parseIntExp(Expression *e, dinteger_t &res) { + IntegerExp *i = NULL; - e = e->optimize(WANTvalue); - if (e->op == TOKint64 && (i = static_cast(e))) - { - res = i->getInteger(); - return true; - } - return false; + e = e->optimize(WANTvalue); + if (e->op == TOKint64 && (i = static_cast(e))) { + res = i->getInteger(); + return true; + } + return false; } -Pragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl, std::string &arg1str) -{ - Identifier *ident = decl->ident; - Expressions *args = decl->args; - Expression *expr = (args && args->dim > 0) ? (*args)[0]->semantic(sc) : 0; +Pragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl, std::string &arg1str) { + Identifier *ident = decl->ident; + Expressions *args = decl->args; + Expression *expr = (args && args->dim > 0) ? (*args)[0]->semantic(sc) : 0; - // pragma(LDC_intrinsic, "string") { funcdecl(s) } - if (ident == Id::LDC_intrinsic) - { - if (!args || args->dim != 1 || !parseStringExp(expr, arg1str)) - { - error(Loc(), "requires exactly 1 string literal parameter"); - fatal(); - } - - // Recognize LDC-specific pragmas. - struct LdcIntrinsic - { - std::string name; - Pragma pragma; - }; - static LdcIntrinsic ldcIntrinsic[] = { - { "bitop.bt", LLVMbitop_bt }, - { "bitop.btc", LLVMbitop_btc }, - { "bitop.btr", LLVMbitop_btr }, - { "bitop.bts", LLVMbitop_bts }, - { "bitop.vld", LLVMbitop_vld }, - { "bitop.vst", LLVMbitop_vst }, - }; - - static std::string prefix = "ldc."; - if (arg1str.length() > prefix.length() && - std::equal(prefix.begin(), prefix.end(), arg1str.begin())) - { - // Got ldc prefix, binary search through ldcIntrinsic. - std::string name(arg1str.begin() + prefix.length(), arg1str.end()); - size_t i = 0, j = sizeof(ldcIntrinsic) / sizeof(ldcIntrinsic[0]); - do - { - size_t k = (i + j) / 2; - int cmp = name.compare(ldcIntrinsic[k].name); - if (!cmp) - return ldcIntrinsic[k].pragma; - else if (cmp < 0) - j = k; - else - i = k + 1; - } - while (i != j); - } - - return LLVMintrinsic; + // pragma(LDC_intrinsic, "string") { funcdecl(s) } + if (ident == Id::LDC_intrinsic) { + if (!args || args->dim != 1 || !parseStringExp(expr, arg1str)) { + error(Loc(), "requires exactly 1 string literal parameter"); + fatal(); } - // pragma(LDC_global_crt_ctor [, priority]) { funcdecl(s) } - else if (ident == Id::LDC_global_crt_ctor || ident == Id::LDC_global_crt_dtor) - { - dinteger_t priority; - if (args) - { - if (args->dim != 1 || !parseIntExp(expr, priority)) - { - error(Loc(), "requires at most 1 integer literal parameter"); - fatal(); - } - if (priority > 65535) - { - error(Loc(), "priority may not be greater then 65535"); - priority = 65535; - } - } + // Recognize LDC-specific pragmas. + struct LdcIntrinsic { + std::string name; + Pragma pragma; + }; + static LdcIntrinsic ldcIntrinsic[] = { + {"bitop.bt", LLVMbitop_bt}, {"bitop.btc", LLVMbitop_btc}, + {"bitop.btr", LLVMbitop_btr}, {"bitop.bts", LLVMbitop_bts}, + {"bitop.vld", LLVMbitop_vld}, {"bitop.vst", LLVMbitop_vst}, + }; + + static std::string prefix = "ldc."; + if (arg1str.length() > prefix.length() && + std::equal(prefix.begin(), prefix.end(), arg1str.begin())) { + // Got ldc prefix, binary search through ldcIntrinsic. + std::string name(arg1str.begin() + prefix.length(), arg1str.end()); + size_t i = 0, j = sizeof(ldcIntrinsic) / sizeof(ldcIntrinsic[0]); + do { + size_t k = (i + j) / 2; + int cmp = name.compare(ldcIntrinsic[k].name); + if (!cmp) + return ldcIntrinsic[k].pragma; + else if (cmp < 0) + j = k; else - priority = 65535; - char buf[8]; - sprintf(buf, "%llu", static_cast(priority)); - arg1str = std::string(buf); - return ident == Id::LDC_global_crt_ctor ? LLVMglobal_crt_ctor : LLVMglobal_crt_dtor; + i = k + 1; + } while (i != j); } - // pragma(LDC_no_typeinfo) { typedecl(s) } - else if (ident == Id::LDC_no_typeinfo) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMno_typeinfo; - } + return LLVMintrinsic; + } - // pragma(LDC_no_moduleinfo) ; - else if (ident == Id::LDC_no_moduleinfo) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - sc->module->noModuleInfo = true; - return LLVMignore; - } - - // pragma(LDC_alloca) { funcdecl(s) } - else if (ident == Id::LDC_alloca) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMalloca; - } - - // pragma(LDC_va_start) { templdecl(s) } - else if (ident == Id::LDC_va_start) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMva_start; - } - - // pragma(LDC_va_copy) { funcdecl(s) } - else if (ident == Id::LDC_va_copy) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMva_copy; - } - - // pragma(LDC_va_end) { funcdecl(s) } - else if (ident == Id::LDC_va_end) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMva_end; - } - - // pragma(LDC_va_arg) { templdecl(s) } - else if (ident == Id::LDC_va_arg) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMva_arg; - } - - // pragma(LDC_fence) { funcdecl(s) } - else if (ident == Id::LDC_fence) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMfence; - } - - // pragma(LDC_atomic_load) { templdecl(s) } - else if (ident == Id::LDC_atomic_load) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMatomic_load; - } - - // pragma(LDC_atomic_store) { templdecl(s) } - else if (ident == Id::LDC_atomic_store) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMatomic_store; - } - - // pragma(LDC_atomic_cmp_xchg) { templdecl(s) } - else if (ident == Id::LDC_atomic_cmp_xchg) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMatomic_cmp_xchg; - } - - // pragma(LDC_atomic_rmw, "string") { templdecl(s) } - else if (ident == Id::LDC_atomic_rmw) - { - if (!args || args->dim != 1 || !parseStringExp(expr, arg1str)) - { - error(Loc(), "requires exactly 1 string literal parameter"); - fatal(); - } - return LLVMatomic_rmw; - } - - // pragma(LDC_verbose); - else if (ident == Id::LDC_verbose) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - sc->module->llvmForceLogging = true; - return LLVMignore; - } - - // pragma(LDC_inline_asm) { templdecl(s) } - else if (ident == Id::LDC_inline_asm) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMinline_asm; - } - - // pragma(LDC_inline_ir) { templdecl(s) } - else if (ident == Id::LDC_inline_ir) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMinline_ir; - } - - // pragma(LDC_extern_weak) { vardecl(s) } - else if (ident == Id::LDC_extern_weak) - { - if (args && args->dim > 0) - { - error(Loc(), "takes no parameters"); - fatal(); - } - return LLVMextern_weak; - } - - return LLVMnone; -} - -void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s, - Pragma llvm_internal, const std::string &arg1str) -{ - if (llvm_internal == LLVMnone || llvm_internal == LLVMignore) - return; - - if (s->llvmInternal) - { - error(Loc(), "multiple LDC specific pragmas not allowed not affect the same " - "declaration ('%s' at '%s')", s->toChars(), s->loc.toChars()); + // pragma(LDC_global_crt_ctor [, priority]) { funcdecl(s) } + else if (ident == Id::LDC_global_crt_ctor || + ident == Id::LDC_global_crt_dtor) { + dinteger_t priority; + if (args) { + if (args->dim != 1 || !parseIntExp(expr, priority)) { + error(Loc(), "requires at most 1 integer literal parameter"); fatal(); + } + if (priority > 65535) { + error(Loc(), "priority may not be greater then 65535"); + priority = 65535; + } + } else + priority = 65535; + char buf[8]; + sprintf(buf, "%llu", static_cast(priority)); + arg1str = std::string(buf); + return ident == Id::LDC_global_crt_ctor ? LLVMglobal_crt_ctor + : LLVMglobal_crt_dtor; + } + + // pragma(LDC_no_typeinfo) { typedecl(s) } + else if (ident == Id::LDC_no_typeinfo) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); } + return LLVMno_typeinfo; + } - Identifier *ident = decl->ident; - - switch(llvm_internal) - { - case LLVMintrinsic: - if (FuncDeclaration* fd = s->isFuncDeclaration()) - { - fd->llvmInternal = llvm_internal; - fd->intrinsicName = arg1str; - fd->mangleOverride = strdup(fd->intrinsicName.c_str()); - } - else if (TemplateDeclaration* td = s->isTemplateDeclaration()) - { - td->llvmInternal = llvm_internal; - td->intrinsicName = arg1str; - } - else - { - error(s->loc, "the '%s' pragma is only allowed on function or template declarations", - ident->toChars()); - fatal(); - } - break; - - case LLVMglobal_crt_ctor: - case LLVMglobal_crt_dtor: - if (FuncDeclaration* fd = s->isFuncDeclaration()) - { - assert(fd->type->ty == Tfunction); - TypeFunction* type = static_cast(fd->type); - Type* retType = type->next; - if (retType->ty != Tvoid || type->parameters->dim > 0 || ( - fd->isAggregateMember() - && !fd->isStatic())) { - error(s->loc, "the '%s' pragma is only allowed on void functions which take no arguments", - ident->toChars()); - fd->llvmInternal = LLVMnone; - break; - } - - fd->llvmInternal = llvm_internal; - fd->priority = std::atoi(arg1str.c_str()); - } - else - { - error(s->loc, "the '%s' pragma is only allowed on function declarations", - ident->toChars()); - s->llvmInternal = LLVMnone; - } - break; - - case LLVMatomic_rmw: - if (TemplateDeclaration* td = s->isTemplateDeclaration()) - { - td->llvmInternal = llvm_internal; - td->intrinsicName = arg1str; - } - else - { - error(s->loc, "the '%s' pragma is only allowed on template declarations", - ident->toChars()); - fatal(); - } - break; - - case LLVMva_start: - case LLVMva_arg: - case LLVMatomic_load: - case LLVMatomic_store: - case LLVMatomic_cmp_xchg: - if (TemplateDeclaration* td = s->isTemplateDeclaration()) - { - if (td->parameters->dim != 1) - { - error(s->loc, "the '%s' pragma template must have exactly one template parameter", - ident->toChars()); - fatal(); - } - else if (!td->onemember) - { - error(s->loc, "the '%s' pragma template must have exactly one member", - ident->toChars()); - fatal(); - } - else if (td->overnext || td->overroot) - { - error(s->loc, "the '%s' pragma template must not be overloaded", - ident->toChars()); - fatal(); - } - td->llvmInternal = llvm_internal; - } - else - { - error(s->loc, "the '%s' pragma is only allowed on template declarations", - ident->toChars()); - fatal(); - } - break; - - case LLVMva_copy: - case LLVMva_end: - case LLVMfence: - case LLVMbitop_bt: - case LLVMbitop_btc: - case LLVMbitop_btr: - case LLVMbitop_bts: - case LLVMbitop_vld: - case LLVMbitop_vst: - if (FuncDeclaration* fd = s->isFuncDeclaration()) - { - fd->llvmInternal = llvm_internal; - } - else - { - error(s->loc, "the '%s' pragma is only allowed on function declarations", - ident->toChars()); - fatal(); - } - break; - - case LLVMno_typeinfo: - s->llvmInternal = llvm_internal; - break; - - case LLVMalloca: - if (FuncDeclaration* fd = s->isFuncDeclaration()) - { - fd->llvmInternal = llvm_internal; - } - else - { - error(s->loc, "the '%s' pragma must only be used on function declarations " - "of type 'void* function(uint nbytes)'", ident->toChars()); - fatal(); - } - break; - - case LLVMinline_asm: - if (TemplateDeclaration* td = s->isTemplateDeclaration()) - { - if (td->parameters->dim > 1) - { - error(s->loc, "the '%s' pragma template must have exactly zero or one " - "template parameters", ident->toChars()); - fatal(); - } - else if (!td->onemember) - { - error(s->loc, "the '%s' pragma template must have exactly one member", - ident->toChars()); - fatal(); - } - td->llvmInternal = llvm_internal; - } - else - { - error(s->loc, "the '%s' pragma is only allowed on template declarations", - ident->toChars()); - fatal(); - } - break; - - case LLVMinline_ir: - if (TemplateDeclaration* td = s->isTemplateDeclaration()) - { - Dsymbol* member = td->onemember; - if (!member) - { - error(s->loc, "the '%s' pragma template must have exactly one member", - ident->toChars()); - fatal(); - } - FuncDeclaration* fun = member->isFuncDeclaration(); - if (!fun) - { - error(s->loc, "the '%s' pragma template's member must be a function declaration", - ident->toChars()); - fatal(); - } - - TemplateParameters& params = *td->parameters; - bool valid_params = - params.dim == 3 && params[1]->isTemplateTypeParameter() && - params[2]->isTemplateTupleParameter(); - - if(valid_params) - { - TemplateValueParameter* p0 = params[0]->isTemplateValueParameter(); - valid_params = valid_params && p0 && p0->valType == Type::tstring; - } - - if(!valid_params) - { - error(s->loc, "the '%s' pragma template must have exactly three parameters: " - "a string, a type and a type tuple", ident->toChars()); - fatal(); - } - - td->llvmInternal = llvm_internal; - } - else - { - error(s->loc, "the '%s' pragma is only allowed on template declarations", - ident->toChars()); - fatal(); - } - break; - - case LLVMextern_weak: - if (VarDeclaration* vd = s->isVarDeclaration()) - { - if (!vd->isDataseg() || !(vd->storage_class & STCextern)) { - error(s->loc, "'%s' requires storage class 'extern'", - ident->toChars()); - fatal(); - } - - // It seems like the interaction between weak symbols and thread-local - // storage is not well-defined (the address of an undefined weak TLS - // symbol is non-zero on the ELF static TLS model on Linux x86_64). - // Thus, just disallow this altogether. - if (vd->isThreadlocal()) { - error(s->loc, "'%s' cannot be applied to thread-local variable '%s'", - ident->toChars(), vd->toPrettyChars()); - fatal(); - } - vd->llvmInternal = llvm_internal; - } - else - { - // Currently, things like "pragma(LDC_extern_weak) extern int foo;" - // fail because 'extern' creates an intermediate - // StorageClassDeclaration. This might eventually be fixed by making - // extern_weak a proper storage class. - error(s->loc, "the '%s' pragma can only be specified directly on " - "variable declarations for now", ident->toChars()); - fatal(); - } - break; - - default: - warning(s->loc, - "the LDC specific pragma '%s' is not yet implemented, ignoring", - ident->toChars()); + // pragma(LDC_no_moduleinfo) ; + else if (ident == Id::LDC_no_moduleinfo) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); } + sc->module->noModuleInfo = true; + return LLVMignore; + } + + // pragma(LDC_alloca) { funcdecl(s) } + else if (ident == Id::LDC_alloca) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMalloca; + } + + // pragma(LDC_va_start) { templdecl(s) } + else if (ident == Id::LDC_va_start) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMva_start; + } + + // pragma(LDC_va_copy) { funcdecl(s) } + else if (ident == Id::LDC_va_copy) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMva_copy; + } + + // pragma(LDC_va_end) { funcdecl(s) } + else if (ident == Id::LDC_va_end) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMva_end; + } + + // pragma(LDC_va_arg) { templdecl(s) } + else if (ident == Id::LDC_va_arg) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMva_arg; + } + + // pragma(LDC_fence) { funcdecl(s) } + else if (ident == Id::LDC_fence) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMfence; + } + + // pragma(LDC_atomic_load) { templdecl(s) } + else if (ident == Id::LDC_atomic_load) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMatomic_load; + } + + // pragma(LDC_atomic_store) { templdecl(s) } + else if (ident == Id::LDC_atomic_store) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMatomic_store; + } + + // pragma(LDC_atomic_cmp_xchg) { templdecl(s) } + else if (ident == Id::LDC_atomic_cmp_xchg) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMatomic_cmp_xchg; + } + + // pragma(LDC_atomic_rmw, "string") { templdecl(s) } + else if (ident == Id::LDC_atomic_rmw) { + if (!args || args->dim != 1 || !parseStringExp(expr, arg1str)) { + error(Loc(), "requires exactly 1 string literal parameter"); + fatal(); + } + return LLVMatomic_rmw; + } + + // pragma(LDC_verbose); + else if (ident == Id::LDC_verbose) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + sc->module->llvmForceLogging = true; + return LLVMignore; + } + + // pragma(LDC_inline_asm) { templdecl(s) } + else if (ident == Id::LDC_inline_asm) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMinline_asm; + } + + // pragma(LDC_inline_ir) { templdecl(s) } + else if (ident == Id::LDC_inline_ir) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMinline_ir; + } + + // pragma(LDC_extern_weak) { vardecl(s) } + else if (ident == Id::LDC_extern_weak) { + if (args && args->dim > 0) { + error(Loc(), "takes no parameters"); + fatal(); + } + return LLVMextern_weak; + } + + return LLVMnone; } -bool DtoIsIntrinsic(FuncDeclaration *fd) -{ - switch (fd->llvmInternal) - { - case LLVMintrinsic: - case LLVMalloca: - case LLVMfence: - case LLVMatomic_store: - case LLVMatomic_load: - case LLVMatomic_cmp_xchg: - case LLVMatomic_rmw: - case LLVMbitop_bt: - case LLVMbitop_btc: - case LLVMbitop_btr: - case LLVMbitop_bts: - case LLVMbitop_vld: - case LLVMbitop_vst: - return true; +void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s, Pragma llvm_internal, + const std::string &arg1str) { + if (llvm_internal == LLVMnone || llvm_internal == LLVMignore) + return; - default: - return DtoIsVaIntrinsic(fd); + if (s->llvmInternal) { + error(Loc(), + "multiple LDC specific pragmas not allowed not affect the same " + "declaration ('%s' at '%s')", + s->toChars(), s->loc.toChars()); + fatal(); + } + + Identifier *ident = decl->ident; + + switch (llvm_internal) { + case LLVMintrinsic: + if (FuncDeclaration *fd = s->isFuncDeclaration()) { + fd->llvmInternal = llvm_internal; + fd->intrinsicName = arg1str; + fd->mangleOverride = strdup(fd->intrinsicName.c_str()); + } else if (TemplateDeclaration *td = s->isTemplateDeclaration()) { + td->llvmInternal = llvm_internal; + td->intrinsicName = arg1str; + } else { + error(s->loc, "the '%s' pragma is only allowed on function or template " + "declarations", + ident->toChars()); + fatal(); } + break; + + case LLVMglobal_crt_ctor: + case LLVMglobal_crt_dtor: + if (FuncDeclaration *fd = s->isFuncDeclaration()) { + assert(fd->type->ty == Tfunction); + TypeFunction *type = static_cast(fd->type); + Type *retType = type->next; + if (retType->ty != Tvoid || type->parameters->dim > 0 || + (fd->isAggregateMember() && !fd->isStatic())) { + error(s->loc, "the '%s' pragma is only allowed on void functions which " + "take no arguments", + ident->toChars()); + fd->llvmInternal = LLVMnone; + break; + } + + fd->llvmInternal = llvm_internal; + fd->priority = std::atoi(arg1str.c_str()); + } else { + error(s->loc, "the '%s' pragma is only allowed on function declarations", + ident->toChars()); + s->llvmInternal = LLVMnone; + } + break; + + case LLVMatomic_rmw: + if (TemplateDeclaration *td = s->isTemplateDeclaration()) { + td->llvmInternal = llvm_internal; + td->intrinsicName = arg1str; + } else { + error(s->loc, "the '%s' pragma is only allowed on template declarations", + ident->toChars()); + fatal(); + } + break; + + case LLVMva_start: + case LLVMva_arg: + case LLVMatomic_load: + case LLVMatomic_store: + case LLVMatomic_cmp_xchg: + if (TemplateDeclaration *td = s->isTemplateDeclaration()) { + if (td->parameters->dim != 1) { + error( + s->loc, + "the '%s' pragma template must have exactly one template parameter", + ident->toChars()); + fatal(); + } else if (!td->onemember) { + error(s->loc, "the '%s' pragma template must have exactly one member", + ident->toChars()); + fatal(); + } else if (td->overnext || td->overroot) { + error(s->loc, "the '%s' pragma template must not be overloaded", + ident->toChars()); + fatal(); + } + td->llvmInternal = llvm_internal; + } else { + error(s->loc, "the '%s' pragma is only allowed on template declarations", + ident->toChars()); + fatal(); + } + break; + + case LLVMva_copy: + case LLVMva_end: + case LLVMfence: + case LLVMbitop_bt: + case LLVMbitop_btc: + case LLVMbitop_btr: + case LLVMbitop_bts: + case LLVMbitop_vld: + case LLVMbitop_vst: + if (FuncDeclaration *fd = s->isFuncDeclaration()) { + fd->llvmInternal = llvm_internal; + } else { + error(s->loc, "the '%s' pragma is only allowed on function declarations", + ident->toChars()); + fatal(); + } + break; + + case LLVMno_typeinfo: + s->llvmInternal = llvm_internal; + break; + + case LLVMalloca: + if (FuncDeclaration *fd = s->isFuncDeclaration()) { + fd->llvmInternal = llvm_internal; + } else { + error(s->loc, + "the '%s' pragma must only be used on function declarations " + "of type 'void* function(uint nbytes)'", + ident->toChars()); + fatal(); + } + break; + + case LLVMinline_asm: + if (TemplateDeclaration *td = s->isTemplateDeclaration()) { + if (td->parameters->dim > 1) { + error(s->loc, "the '%s' pragma template must have exactly zero or one " + "template parameters", + ident->toChars()); + fatal(); + } else if (!td->onemember) { + error(s->loc, "the '%s' pragma template must have exactly one member", + ident->toChars()); + fatal(); + } + td->llvmInternal = llvm_internal; + } else { + error(s->loc, "the '%s' pragma is only allowed on template declarations", + ident->toChars()); + fatal(); + } + break; + + case LLVMinline_ir: + if (TemplateDeclaration *td = s->isTemplateDeclaration()) { + Dsymbol *member = td->onemember; + if (!member) { + error(s->loc, "the '%s' pragma template must have exactly one member", + ident->toChars()); + fatal(); + } + FuncDeclaration *fun = member->isFuncDeclaration(); + if (!fun) { + error( + s->loc, + "the '%s' pragma template's member must be a function declaration", + ident->toChars()); + fatal(); + } + + TemplateParameters ¶ms = *td->parameters; + bool valid_params = params.dim == 3 && + params[1]->isTemplateTypeParameter() && + params[2]->isTemplateTupleParameter(); + + if (valid_params) { + TemplateValueParameter *p0 = params[0]->isTemplateValueParameter(); + valid_params = valid_params && p0 && p0->valType == Type::tstring; + } + + if (!valid_params) { + error(s->loc, + "the '%s' pragma template must have exactly three parameters: " + "a string, a type and a type tuple", + ident->toChars()); + fatal(); + } + + td->llvmInternal = llvm_internal; + } else { + error(s->loc, "the '%s' pragma is only allowed on template declarations", + ident->toChars()); + fatal(); + } + break; + + case LLVMextern_weak: + if (VarDeclaration *vd = s->isVarDeclaration()) { + if (!vd->isDataseg() || !(vd->storage_class & STCextern)) { + error(s->loc, "'%s' requires storage class 'extern'", ident->toChars()); + fatal(); + } + + // It seems like the interaction between weak symbols and thread-local + // storage is not well-defined (the address of an undefined weak TLS + // symbol is non-zero on the ELF static TLS model on Linux x86_64). + // Thus, just disallow this altogether. + if (vd->isThreadlocal()) { + error(s->loc, "'%s' cannot be applied to thread-local variable '%s'", + ident->toChars(), vd->toPrettyChars()); + fatal(); + } + vd->llvmInternal = llvm_internal; + } else { + // Currently, things like "pragma(LDC_extern_weak) extern int foo;" + // fail because 'extern' creates an intermediate + // StorageClassDeclaration. This might eventually be fixed by making + // extern_weak a proper storage class. + error(s->loc, "the '%s' pragma can only be specified directly on " + "variable declarations for now", + ident->toChars()); + fatal(); + } + break; + + default: + warning(s->loc, + "the LDC specific pragma '%s' is not yet implemented, ignoring", + ident->toChars()); + } } -bool DtoIsVaIntrinsic(FuncDeclaration *fd) -{ - return (fd->llvmInternal == LLVMva_start || - fd->llvmInternal == LLVMva_copy || - fd->llvmInternal == LLVMva_end); +bool DtoIsIntrinsic(FuncDeclaration *fd) { + switch (fd->llvmInternal) { + case LLVMintrinsic: + case LLVMalloca: + case LLVMfence: + case LLVMatomic_store: + case LLVMatomic_load: + case LLVMatomic_cmp_xchg: + case LLVMatomic_rmw: + case LLVMbitop_bt: + case LLVMbitop_btc: + case LLVMbitop_btr: + case LLVMbitop_bts: + case LLVMbitop_vld: + case LLVMbitop_vst: + return true; + + default: + return DtoIsVaIntrinsic(fd); + } +} + +bool DtoIsVaIntrinsic(FuncDeclaration *fd) { + return (fd->llvmInternal == LLVMva_start || fd->llvmInternal == LLVMva_copy || + fd->llvmInternal == LLVMva_end); } diff --git a/gen/pragma.h b/gen/pragma.h index ddc093ab6a..094a4f7038 100644 --- a/gen/pragma.h +++ b/gen/pragma.h @@ -21,38 +21,37 @@ class FuncDeclaration; class Dsymbol; struct Scope; -enum Pragma -{ - LLVMnone, // Not an LDC pragma. - LLVMignore, // Pragma has already been processed in DtoGetPragma, ignore. - LLVMintrinsic, - LLVMglobal_crt_ctor, - LLVMglobal_crt_dtor, - LLVMno_typeinfo, - LLVMalloca, - LLVMva_start, - LLVMva_copy, - LLVMva_end, - LLVMva_arg, - LLVMinline_asm, - LLVMinline_ir, - LLVMfence, - LLVMatomic_store, - LLVMatomic_load, - LLVMatomic_cmp_xchg, - LLVMatomic_rmw, - LLVMbitop_bt, - LLVMbitop_btc, - LLVMbitop_btr, - LLVMbitop_bts, - LLVMbitop_vld, - LLVMbitop_vst, - LLVMextern_weak +enum Pragma { + LLVMnone, // Not an LDC pragma. + LLVMignore, // Pragma has already been processed in DtoGetPragma, ignore. + LLVMintrinsic, + LLVMglobal_crt_ctor, + LLVMglobal_crt_dtor, + LLVMno_typeinfo, + LLVMalloca, + LLVMva_start, + LLVMva_copy, + LLVMva_end, + LLVMva_arg, + LLVMinline_asm, + LLVMinline_ir, + LLVMfence, + LLVMatomic_store, + LLVMatomic_load, + LLVMatomic_cmp_xchg, + LLVMatomic_rmw, + LLVMbitop_bt, + LLVMbitop_btc, + LLVMbitop_btr, + LLVMbitop_bts, + LLVMbitop_vld, + LLVMbitop_vst, + LLVMextern_weak }; Pragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl, std::string &arg1str); -void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *sym, - Pragma llvm_internal, const std::string &arg1str); +void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *sym, Pragma llvm_internal, + const std::string &arg1str); bool DtoIsIntrinsic(FuncDeclaration *fd); bool DtoIsVaIntrinsic(FuncDeclaration *fd); diff --git a/gen/programs.cpp b/gen/programs.cpp index b69d400715..bfc6bb0327 100644 --- a/gen/programs.cpp +++ b/gen/programs.cpp @@ -14,64 +14,56 @@ using namespace llvm; -static cl::opt gcc("gcc", - cl::desc("GCC to use for assembling and linking"), - cl::Hidden, - cl::ZeroOrMore); +static cl::opt + gcc("gcc", cl::desc("GCC to use for assembling and linking"), cl::Hidden, + cl::ZeroOrMore); -static cl::opt ar("ar", - cl::desc("Archiver"), - cl::Hidden, - cl::ZeroOrMore); +static cl::opt ar("ar", cl::desc("Archiver"), cl::Hidden, + cl::ZeroOrMore); -static std::string findProgramByName(const std::string& name) -{ +static std::string findProgramByName(const std::string &name) { #if LDC_LLVM_VER >= 306 - llvm::ErrorOr res = llvm::sys::findProgramByName(name); - return res ? res.get() : std::string(); + llvm::ErrorOr res = llvm::sys::findProgramByName(name); + return res ? res.get() : std::string(); #else - return llvm::sys::FindProgramByName(name); + return llvm::sys::FindProgramByName(name); #endif } -static std::string getProgram(const char* name, const cl::opt* opt, const char* envVar = NULL) -{ - std::string path; - const char *prog = NULL; +static std::string getProgram(const char *name, const cl::opt *opt, + const char *envVar = NULL) { + std::string path; + const char *prog = NULL; - if (opt && opt->getNumOccurrences() > 0 && opt->length() > 0 && (prog = opt->c_str())) - path = findProgramByName(prog); + if (opt && opt->getNumOccurrences() > 0 && opt->length() > 0 && + (prog = opt->c_str())) + path = findProgramByName(prog); - if (path.empty() && envVar && (prog = getenv(envVar)) && prog[0] != '\0') - path = findProgramByName(prog); + if (path.empty() && envVar && (prog = getenv(envVar)) && prog[0] != '\0') + path = findProgramByName(prog); - if (path.empty()) - path = findProgramByName(name); + if (path.empty()) + path = findProgramByName(name); - if (path.empty()) { - error(Loc(), "failed to locate %s", name); - fatal(); - } + if (path.empty()) { + error(Loc(), "failed to locate %s", name); + fatal(); + } - return path; + return path; } -std::string getProgram(const char* name, const char* envVar) -{ - return getProgram(name, NULL, envVar); +std::string getProgram(const char *name, const char *envVar) { + return getProgram(name, NULL, envVar); } -std::string getGcc() -{ +std::string getGcc() { #if defined(__FreeBSD__) && __FreeBSD__ >= 10 - // Default compiler on FreeBSD 10 is clang - return getProgram("clang", &gcc, "CC"); + // Default compiler on FreeBSD 10 is clang + return getProgram("clang", &gcc, "CC"); #else - return getProgram("gcc", &gcc, "CC"); + return getProgram("gcc", &gcc, "CC"); #endif } -std::string getArchiver() -{ - return getProgram("ar", &ar); -} +std::string getArchiver() { return getProgram("ar", &ar); } diff --git a/gen/programs.h b/gen/programs.h index 24c3b7353e..43f2b23719 100644 --- a/gen/programs.h +++ b/gen/programs.h @@ -16,7 +16,7 @@ #include -std::string getProgram(const char* name, const char* envVar = 0); +std::string getProgram(const char *name, const char *envVar = 0); std::string getGcc(); std::string getArchiver(); diff --git a/gen/rttibuilder.cpp b/gen/rttibuilder.cpp index 9793b98b1d..fd3dbe80b5 100644 --- a/gen/rttibuilder.cpp +++ b/gen/rttibuilder.cpp @@ -20,187 +20,148 @@ #include "ir/iraggr.h" #include "ir/irfunction.h" -RTTIBuilder::RTTIBuilder(AggregateDeclaration* base_class) -{ - DtoResolveDsymbol(base_class); +RTTIBuilder::RTTIBuilder(AggregateDeclaration *base_class) { + DtoResolveDsymbol(base_class); - base = base_class; - basetype = static_cast(base->type); + base = base_class; + basetype = static_cast(base->type); - baseir = getIrAggr(base); - assert(baseir && "no IrStruct for TypeInfo base class"); + baseir = getIrAggr(base); + assert(baseir && "no IrStruct for TypeInfo base class"); - prevFieldEnd = 0; + prevFieldEnd = 0; - if (base->isClassDeclaration()) { - // just start with adding the vtbl - push(baseir->getVtblSymbol()); - // and monitor - push_null_vp(); - } + if (base->isClassDeclaration()) { + // just start with adding the vtbl + push(baseir->getVtblSymbol()); + // and monitor + push_null_vp(); + } } -void RTTIBuilder::push(llvm::Constant* C) -{ - // We need to explicitly zero any padding bytes as per TDPL §7.1.1 (and - // also match the struct type lowering code here). - const uint64_t fieldStart = llvm::RoundUpToAlignment(prevFieldEnd, - gDataLayout->getABITypeAlignment(C->getType())); - const uint64_t paddingBytes = fieldStart - prevFieldEnd; - if (paddingBytes) { - llvm::Type *const padding = llvm::ArrayType::get( - llvm::Type::getInt8Ty(gIR->context()), paddingBytes); - inits.push_back(llvm::Constant::getNullValue(padding)); - } - inits.push_back(C); - prevFieldEnd = fieldStart + gDataLayout->getTypeAllocSize(C->getType()); +void RTTIBuilder::push(llvm::Constant *C) { + // We need to explicitly zero any padding bytes as per TDPL §7.1.1 (and + // also match the struct type lowering code here). + const uint64_t fieldStart = llvm::RoundUpToAlignment( + prevFieldEnd, gDataLayout->getABITypeAlignment(C->getType())); + const uint64_t paddingBytes = fieldStart - prevFieldEnd; + if (paddingBytes) { + llvm::Type *const padding = llvm::ArrayType::get( + llvm::Type::getInt8Ty(gIR->context()), paddingBytes); + inits.push_back(llvm::Constant::getNullValue(padding)); + } + inits.push_back(C); + prevFieldEnd = fieldStart + gDataLayout->getTypeAllocSize(C->getType()); } -void RTTIBuilder::push_null(Type* T) -{ - push(getNullValue(DtoType(T))); +void RTTIBuilder::push_null(Type *T) { push(getNullValue(DtoType(T))); } + +void RTTIBuilder::push_null_vp() { push(getNullValue(getVoidPtrType())); } + +void RTTIBuilder::push_typeinfo(Type *t) { push(DtoTypeInfoOf(t, true)); } + +void RTTIBuilder::push_classinfo(ClassDeclaration *cd) { + push(getIrAggr(cd)->getClassInfoSymbol()); } -void RTTIBuilder::push_null_vp() -{ - push(getNullValue(getVoidPtrType())); +void RTTIBuilder::push_string(const char *str) { push(DtoConstString(str)); } + +void RTTIBuilder::push_null_void_array() { + LLType *T = DtoType(Type::tvoid->arrayOf()); + push(getNullValue(T)); } -void RTTIBuilder::push_typeinfo(Type* t) -{ - push(DtoTypeInfoOf(t, true)); +void RTTIBuilder::push_void_array(uint64_t dim, llvm::Constant *ptr) { + push(DtoConstSlice(DtoConstSize_t(dim), DtoBitCast(ptr, getVoidPtrType()))); } -void RTTIBuilder::push_classinfo(ClassDeclaration* cd) -{ - push(getIrAggr(cd)->getClassInfoSymbol()); +void RTTIBuilder::push_void_array(llvm::Constant *CI, Type *valtype, + Dsymbol *mangle_sym) { + std::string initname(mangle(mangle_sym)); + initname.append(".rtti.voidarr.data"); + + LLGlobalVariable *G = new LLGlobalVariable( + gIR->module, CI->getType(), true, TYPEINFO_LINKAGE_TYPE, CI, initname); + SET_COMDAT(G, gIR->module); + G->setAlignment(DtoAlignment(valtype)); + + push_void_array(getTypePaddedSize(CI->getType()), G); } -void RTTIBuilder::push_string(const char* str) -{ - push(DtoConstString(str)); +void RTTIBuilder::push_array(llvm::Constant *CI, uint64_t dim, Type *valtype, + Dsymbol *mangle_sym) { + std::string tmpStr(valtype->arrayOf()->toChars()); + tmpStr.erase(remove(tmpStr.begin(), tmpStr.end(), '['), tmpStr.end()); + tmpStr.erase(remove(tmpStr.begin(), tmpStr.end(), ']'), tmpStr.end()); + tmpStr.append("arr"); + + std::string initname(mangle_sym ? mangle(mangle_sym) : ".ldc"); + initname.append(".rtti."); + initname.append(tmpStr); + initname.append(".data"); + + LLGlobalVariable *G = new LLGlobalVariable( + gIR->module, CI->getType(), true, TYPEINFO_LINKAGE_TYPE, CI, initname); + SET_COMDAT(G, gIR->module); + G->setAlignment(DtoAlignment(valtype)); + + push_array(dim, DtoBitCast(G, DtoType(valtype->pointerTo()))); } -void RTTIBuilder::push_null_void_array() -{ - LLType* T = DtoType(Type::tvoid->arrayOf()); - push(getNullValue(T)); +void RTTIBuilder::push_array(uint64_t dim, llvm::Constant *ptr) { + push(DtoConstSlice(DtoConstSize_t(dim), ptr)); } -void RTTIBuilder::push_void_array(uint64_t dim, llvm::Constant* ptr) -{ - push(DtoConstSlice( - DtoConstSize_t(dim), - DtoBitCast(ptr, getVoidPtrType()) - )); +void RTTIBuilder::push_uint(unsigned u) { push(DtoConstUint(u)); } + +void RTTIBuilder::push_size(uint64_t s) { push(DtoConstSize_t(s)); } + +void RTTIBuilder::push_size_as_vp(uint64_t s) { + push(llvm::ConstantExpr::getIntToPtr(DtoConstSize_t(s), getVoidPtrType())); } -void RTTIBuilder::push_void_array(llvm::Constant* CI, Type* valtype, Dsymbol* mangle_sym) -{ - std::string initname(mangle(mangle_sym)); - initname.append(".rtti.voidarr.data"); - - LLGlobalVariable* G = new LLGlobalVariable( - gIR->module, CI->getType(), true, TYPEINFO_LINKAGE_TYPE, CI, initname); - SET_COMDAT(G, gIR->module); - G->setAlignment(DtoAlignment(valtype)); - - push_void_array(getTypePaddedSize(CI->getType()), G); +void RTTIBuilder::push_funcptr(FuncDeclaration *fd, Type *castto) { + if (fd) { + DtoResolveFunction(fd); + LLConstant *F = getIrFunc(fd)->func; + if (castto) + F = DtoBitCast(F, DtoType(castto)); + push(F); + } else if (castto) { + push_null(castto); + } else { + push_null_vp(); + } } -void RTTIBuilder::push_array(llvm::Constant * CI, uint64_t dim, Type* valtype, Dsymbol * mangle_sym) -{ - std::string tmpStr(valtype->arrayOf()->toChars()); - tmpStr.erase( remove( tmpStr.begin(), tmpStr.end(), '[' ), tmpStr.end() ); - tmpStr.erase( remove( tmpStr.begin(), tmpStr.end(), ']' ), tmpStr.end() ); - tmpStr.append("arr"); +void RTTIBuilder::finalize(IrGlobal *tid) { finalize(tid->type, tid->value); } - std::string initname(mangle_sym ? mangle(mangle_sym) : ".ldc"); - initname.append(".rtti."); - initname.append(tmpStr); - initname.append(".data"); +void RTTIBuilder::finalize(LLType *type, LLValue *value) { + llvm::ArrayRef inits = llvm::makeArrayRef(this->inits); + LLStructType *st = isaStruct(type); + assert(st); - LLGlobalVariable* G = new LLGlobalVariable( - gIR->module, CI->getType(), true, TYPEINFO_LINKAGE_TYPE, CI, initname); - SET_COMDAT(G, gIR->module); - G->setAlignment(DtoAlignment(valtype)); + // set struct body + if (st->isOpaque()) { + const int n = inits.size(); + std::vector types; + types.reserve(n); + for (int i = 0; i < n; ++i) + types.push_back(inits[i]->getType()); + st->setBody(types); + } - push_array(dim, DtoBitCast(G, DtoType(valtype->pointerTo()))); + // create the inititalizer + LLConstant *tiInit = LLConstantStruct::get(st, inits); + + // set the initializer + llvm::GlobalVariable *gvar = llvm::cast(value); + gvar->setInitializer(tiInit); + gvar->setLinkage(TYPEINFO_LINKAGE_TYPE); + SET_COMDAT(gvar, gIR->module); } -void RTTIBuilder::push_array(uint64_t dim, llvm::Constant * ptr) -{ - push(DtoConstSlice(DtoConstSize_t(dim), ptr)); -} - -void RTTIBuilder::push_uint(unsigned u) -{ - push(DtoConstUint(u)); -} - -void RTTIBuilder::push_size(uint64_t s) -{ - push(DtoConstSize_t(s)); -} - -void RTTIBuilder::push_size_as_vp(uint64_t s) -{ - push(llvm::ConstantExpr::getIntToPtr(DtoConstSize_t(s), getVoidPtrType())); -} - -void RTTIBuilder::push_funcptr(FuncDeclaration* fd, Type* castto) -{ - if (fd) - { - DtoResolveFunction(fd); - LLConstant* F = getIrFunc(fd)->func; - if (castto) - F = DtoBitCast(F, DtoType(castto)); - push(F); - } - else if (castto) - { - push_null(castto); - } - else - { - push_null_vp(); - } -} - -void RTTIBuilder::finalize(IrGlobal* tid) -{ - finalize(tid->type, tid->value); -} - -void RTTIBuilder::finalize(LLType* type, LLValue* value) -{ - llvm::ArrayRef inits = llvm::makeArrayRef(this->inits); - LLStructType *st = isaStruct(type); - assert(st); - - // set struct body - if (st->isOpaque()) { - const int n = inits.size(); - std::vector types; - types.reserve(n); - for (int i = 0; i < n; ++i) - types.push_back(inits[i]->getType()); - st->setBody(types); - } - - // create the inititalizer - LLConstant* tiInit = LLConstantStruct::get(st, inits); - - // set the initializer - llvm::GlobalVariable* gvar = llvm::cast(value); - gvar->setInitializer(tiInit); - gvar->setLinkage(TYPEINFO_LINKAGE_TYPE); - SET_COMDAT(gvar, gIR->module); -} - -LLConstant* RTTIBuilder::get_constant(LLStructType *initType) -{ - // just return the inititalizer - return LLConstantStruct::get(initType, inits); +LLConstant *RTTIBuilder::get_constant(LLStructType *initType) { + // just return the inititalizer + return LLConstantStruct::get(initType, inits); } diff --git a/gen/rttibuilder.h b/gen/rttibuilder.h index 236c4291b9..d773b0d12e 100644 --- a/gen/rttibuilder.h +++ b/gen/rttibuilder.h @@ -26,61 +26,64 @@ struct IrGlobal; struct IrAggr; class Type; class TypeClass; -namespace llvm { class StructType; } +namespace llvm { +class StructType; +} -class RTTIBuilder -{ - AggregateDeclaration* base; - TypeClass* basetype; - IrAggr* baseir; +class RTTIBuilder { + AggregateDeclaration *base; + TypeClass *basetype; + IrAggr *baseir; - /// The offset (in bytes) at which the previously pushed field ended. - uint64_t prevFieldEnd; + /// The offset (in bytes) at which the previously pushed field ended. + uint64_t prevFieldEnd; public: - // 15 is enough for any D2 ClassInfo including 64 bit pointer alignment padding - llvm::SmallVector inits; + // 15 is enough for any D2 ClassInfo including 64 bit pointer alignment + // padding + llvm::SmallVector inits; - RTTIBuilder(AggregateDeclaration* base_class); + RTTIBuilder(AggregateDeclaration *base_class); - void push(llvm::Constant* C); - void push_null(Type* T); - void push_null_vp(); - void push_null_void_array(); - void push_uint(unsigned u); - void push_size(uint64_t s); - void push_size_as_vp(uint64_t s); - void push_string(const char* str); - void push_typeinfo(Type* t); - void push_classinfo(ClassDeclaration* cd); + void push(llvm::Constant *C); + void push_null(Type *T); + void push_null_vp(); + void push_null_void_array(); + void push_uint(unsigned u); + void push_size(uint64_t s); + void push_size_as_vp(uint64_t s); + void push_string(const char *str); + void push_typeinfo(Type *t); + void push_classinfo(ClassDeclaration *cd); - /// pushes the function pointer or a null void* if it cannot. - void push_funcptr(FuncDeclaration* fd, Type* castto = NULL); + /// pushes the function pointer or a null void* if it cannot. + void push_funcptr(FuncDeclaration *fd, Type *castto = NULL); - /// pushes the array slice given. - void push_array(uint64_t dim, llvm::Constant * ptr); + /// pushes the array slice given. + void push_array(uint64_t dim, llvm::Constant *ptr); - /// pushes void[] slice, dim is used directly, ptr is cast to void* . - void push_void_array(uint64_t dim, llvm::Constant* ptr); + /// pushes void[] slice, dim is used directly, ptr is cast to void* . + void push_void_array(uint64_t dim, llvm::Constant *ptr); - /// pushes void[] slice with data. - /// CI is the constant initializer the array should point to, the length - /// and ptr are resolved automatically - void push_void_array(llvm::Constant* CI, Type* valtype, Dsymbol* mangle_sym); + /// pushes void[] slice with data. + /// CI is the constant initializer the array should point to, the length + /// and ptr are resolved automatically + void push_void_array(llvm::Constant *CI, Type *valtype, Dsymbol *mangle_sym); - /// pushes valtype[] slice with data. - /// CI is the constant initializer that .ptr should point to - /// dim is .length member directly - /// valtype provides the D element type, .ptr is cast to valtype->pointerTo() - /// mangle_sym provides the mangle prefix for the symbol generated. - void push_array(llvm::Constant* CI, uint64_t dim, Type* valtype, Dsymbol* mangle_sym); + /// pushes valtype[] slice with data. + /// CI is the constant initializer that .ptr should point to + /// dim is .length member directly + /// valtype provides the D element type, .ptr is cast to valtype->pointerTo() + /// mangle_sym provides the mangle prefix for the symbol generated. + void push_array(llvm::Constant *CI, uint64_t dim, Type *valtype, + Dsymbol *mangle_sym); - /// Creates the initializer constant and assigns it to the global. - void finalize(IrGlobal* tid); - void finalize(llvm::Type* type, llvm::Value* value); + /// Creates the initializer constant and assigns it to the global. + void finalize(IrGlobal *tid); + void finalize(llvm::Type *type, llvm::Value *value); - /// Creates the initializer constant and assigns it to the global. - llvm::Constant* get_constant(llvm::StructType* initType); + /// Creates the initializer constant and assigns it to the global. + llvm::Constant *get_constant(llvm::StructType *initType); }; #endif diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 74f8b574bc..d5058c2a6a 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -33,924 +33,915 @@ ////////////////////////////////////////////////////////////////////////////////////////////////// -static llvm::cl::opt nogc("nogc", - llvm::cl::desc("Do not allow code that generates implicit garbage collector calls"), +static llvm::cl::opt nogc( + "nogc", + llvm::cl::desc( + "Do not allow code that generates implicit garbage collector calls"), llvm::cl::ZeroOrMore); ////////////////////////////////////////////////////////////////////////////////////////////////// -static llvm::Module* M = NULL; +static llvm::Module *M = NULL; static void LLVM_D_BuildRuntimeModule(); ////////////////////////////////////////////////////////////////////////////////////////////////// -static void checkForImplicitGCCall(const Loc &loc, const char *name) -{ - if (nogc) - { - static const std::string GCNAMES[] = - { - "_aaDelX", - "_aaGetY", - "_aaKeys", - "_aaRehash", - "_aaValues", - "_d_allocmemory", - "_d_allocmemoryT", - "_d_array_cast_len", - "_d_array_slice_copy", - "_d_arrayappendT", - "_d_arrayappendcTX", - "_d_arrayappendcd", - "_d_arrayappendwd", - "_d_arraycatT", - "_d_arraycatnTX", - "_d_arraysetlengthT", - "_d_arraysetlengthiT", - "_d_assocarrayliteralTX", - "_d_callfinalizer", - "_d_delarray_t", - "_d_delclass", - "_d_delstruct", - "_d_delinterface", - "_d_delmemory", - "_d_newarrayT", - "_d_newarrayiT", - "_d_newarraymTX", - "_d_newarraymiTX", - "_d_newarrayU", - "_d_newclass", - "_d_newitemT", - "_d_newitemiT", - }; - - if (binary_search(&GCNAMES[0], &GCNAMES[sizeof(GCNAMES) / sizeof(std::string)], name)) - { - error(loc, "No implicit garbage collector calls allowed with -nogc option enabled: %s", name); - fatal(); - } - } -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -bool LLVM_D_InitRuntime() -{ - Logger::println("*** Initializing D runtime declarations ***"); - LOG_SCOPE; - - if (!M) - { - LLVM_D_BuildRuntimeModule(); - } - - return true; -} - -void LLVM_D_FreeRuntime() -{ - if (M) { - Logger::println("*** Freeing D runtime declarations ***"); - delete M; - M = NULL; - } -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -llvm::Function* LLVM_D_GetRuntimeFunction(const Loc &loc, llvm::Module& target, const char* name) -{ - checkForImplicitGCCall(loc, name); - - if (!M) { - LLVM_D_InitRuntime(); - } - - LLFunction* fn = target.getFunction(name); - if (fn) - return fn; - - fn = M->getFunction(name); - assert(fn && "Runtime function not found."); - - LLFunctionType* fnty = fn->getFunctionType(); - LLFunction* resfn = llvm::cast(target.getOrInsertFunction(name, fnty)); - resfn->setAttributes(fn->getAttributes()); - resfn->setCallingConv(fn->getCallingConv()); - return resfn; -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -llvm::GlobalVariable* LLVM_D_GetRuntimeGlobal(Loc& loc, llvm::Module& target, const char* name) -{ - LLGlobalVariable* gv = target.getNamedGlobal(name); - if (gv) { - return gv; - } - - checkForImplicitGCCall(loc, name); - - if (!M) { - LLVM_D_InitRuntime(); - } - - LLGlobalVariable* g = M->getNamedGlobal(name); - if (!g) { - error(loc, "Runtime global '%s' was not found", name); - fatal(); - //return NULL; - } - - LLPointerType* t = g->getType(); - return getOrCreateGlobal(loc, target, t->getElementType(), g->isConstant(), - g->getLinkage(), NULL, g->getName()); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -static LLType* rt_ptr(LLType* t) -{ - return getPtrToType(t); -} - -static LLType* rt_array(LLType* elemty) -{ - LLType *types[] = { DtoSize_t(), rt_ptr(elemty) }; - return LLStructType::get(gIR->context(), types, false); -} - -static LLType* rt_dg1() -{ - LLType *types1[] = { rt_ptr(LLType::getInt8Ty(gIR->context())), - rt_ptr(LLType::getInt8Ty(gIR->context())) }; - LLFunctionType* fty = LLFunctionType::get(LLType::getInt32Ty(gIR->context()), types1, false); - - LLType *types[] = { - rt_ptr(LLType::getInt8Ty(gIR->context())), - rt_ptr(fty) +static void checkForImplicitGCCall(const Loc &loc, const char *name) { + if (nogc) { + static const std::string GCNAMES[] = { + "_aaDelX", + "_aaGetY", + "_aaKeys", + "_aaRehash", + "_aaValues", + "_d_allocmemory", + "_d_allocmemoryT", + "_d_array_cast_len", + "_d_array_slice_copy", + "_d_arrayappendT", + "_d_arrayappendcTX", + "_d_arrayappendcd", + "_d_arrayappendwd", + "_d_arraycatT", + "_d_arraycatnTX", + "_d_arraysetlengthT", + "_d_arraysetlengthiT", + "_d_assocarrayliteralTX", + "_d_callfinalizer", + "_d_delarray_t", + "_d_delclass", + "_d_delstruct", + "_d_delinterface", + "_d_delmemory", + "_d_newarrayT", + "_d_newarrayiT", + "_d_newarraymTX", + "_d_newarraymiTX", + "_d_newarrayU", + "_d_newclass", + "_d_newitemT", + "_d_newitemiT", }; - return LLStructType::get(gIR->context(), types, false); + + if (binary_search(&GCNAMES[0], + &GCNAMES[sizeof(GCNAMES) / sizeof(std::string)], name)) { + error(loc, "No implicit garbage collector calls allowed with -nogc " + "option enabled: %s", + name); + fatal(); + } + } } -static LLType* rt_dg2() -{ - LLType *Int8PtrTy = rt_ptr(LLType::getInt8Ty(gIR->context())); - LLType *types1[] = { Int8PtrTy, Int8PtrTy, Int8PtrTy }; - LLFunctionType* fty = LLFunctionType::get(LLType::getInt32Ty(gIR->context()), types1, false); +////////////////////////////////////////////////////////////////////////////////////////////////// - LLType *types[] = { - rt_ptr(LLType::getInt8Ty(gIR->context())), - rt_ptr(fty) - }; - return LLStructType::get(gIR->context(), types, false); +bool LLVM_D_InitRuntime() { + Logger::println("*** Initializing D runtime declarations ***"); + LOG_SCOPE; + + if (!M) { + LLVM_D_BuildRuntimeModule(); + } + + return true; } -template -static void ensureDecl(DECL *decl, const char *msg) -{ - if (!decl || !decl->type) - { - Logger::println("Missing class declaration: %s\n", msg); - error(Loc(), "Missing class declaration: %s", msg); - errorSupplemental(Loc(), "Please check that object.di is included and valid"); - fatal(); - } +void LLVM_D_FreeRuntime() { + if (M) { + Logger::println("*** Freeing D runtime declarations ***"); + delete M; + M = NULL; + } } -static void LLVM_D_BuildRuntimeModule() -{ - Logger::println("building runtime module"); - M = new llvm::Module("ldc internal runtime", gIR->context()); +////////////////////////////////////////////////////////////////////////////////////////////////// - LLType* voidTy = LLType::getVoidTy(gIR->context()); - LLType* boolTy = LLType::getInt1Ty(gIR->context()); - LLType* byteTy = LLType::getInt8Ty(gIR->context()); - LLType* intTy = LLType::getInt32Ty(gIR->context()); - LLType* longTy = LLType::getInt64Ty(gIR->context()); - LLType* sizeTy = DtoSize_t(); +llvm::Function *LLVM_D_GetRuntimeFunction(const Loc &loc, llvm::Module &target, + const char *name) { + checkForImplicitGCCall(loc, name); - LLType* voidPtrTy = rt_ptr(byteTy); - LLType* voidArrayTy = rt_array(byteTy); - LLType* voidArrayPtrTy = rt_ptr(voidArrayTy); - LLType* stringTy = DtoType(Type::tchar->arrayOf()); - LLType* wstringTy = DtoType(Type::twchar->arrayOf()); - LLType* dstringTy = DtoType(Type::tdchar->arrayOf()); + if (!M) { + LLVM_D_InitRuntime(); + } - // Ensure that the declarations exist before creating llvm types for them. - ensureDecl(ClassDeclaration::object, "Object"); - ensureDecl(Type::typeinfoclass, "TypeInfo_Class"); - ensureDecl(Type::dtypeinfo, "DTypeInfo"); - ensureDecl(Type::typeinfoassociativearray, "TypeInfo_AssociativeArray"); - ensureDecl(Module::moduleinfo, "ModuleInfo"); + LLFunction *fn = target.getFunction(name); + if (fn) + return fn; - LLType* objectTy = DtoType(ClassDeclaration::object->type); - LLType* classInfoTy = DtoType(Type::typeinfoclass->type); - LLType* typeInfoTy = DtoType(Type::dtypeinfo->type); - LLType* aaTypeInfoTy = DtoType(Type::typeinfoassociativearray->type); - LLType* moduleInfoPtrTy = rt_ptr(DtoType(Module::moduleinfo->type)); - LLType* aaTy = voidPtrTy; + fn = M->getFunction(name); + assert(fn && "Runtime function not found."); - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // Construct some attribute lists used below (possibly multiple times) - llvm::AttributeSet - NoAttrs, - Attr_NoAlias - = NoAttrs.addAttribute(gIR->context(), 0, llvm::Attribute::NoAlias), - Attr_NoUnwind - = NoAttrs.addAttribute(gIR->context(), ~0U, llvm::Attribute::NoUnwind), - Attr_ReadOnly - = NoAttrs.addAttribute(gIR->context(), ~0U, llvm::Attribute::ReadOnly), - Attr_ReadOnly_NoUnwind - = Attr_ReadOnly.addAttribute(gIR->context(), ~0U, llvm::Attribute::NoUnwind), - Attr_ReadOnly_1_NoCapture - = Attr_ReadOnly.addAttribute(gIR->context(), 1, llvm::Attribute::NoCapture), - Attr_ReadOnly_1_3_NoCapture - = Attr_ReadOnly_1_NoCapture.addAttribute(gIR->context(), 3, llvm::Attribute::NoCapture), - Attr_ReadOnly_NoUnwind_1_NoCapture - = Attr_ReadOnly_1_NoCapture.addAttribute(gIR->context(), ~0U, llvm::Attribute::NoUnwind), - Attr_ReadNone - = NoAttrs.addAttribute(gIR->context(), ~0U, llvm::Attribute::ReadNone), - Attr_1_NoCapture - = NoAttrs.addAttribute(gIR->context(), 1, llvm::Attribute::NoCapture), - Attr_NoAlias_1_NoCapture - = Attr_1_NoCapture.addAttribute(gIR->context(), 0, llvm::Attribute::NoAlias), - Attr_1_2_NoCapture - = Attr_1_NoCapture.addAttribute(gIR->context(), 2, llvm::Attribute::NoCapture), - Attr_1_3_NoCapture - = Attr_1_NoCapture.addAttribute(gIR->context(), 3, llvm::Attribute::NoCapture), - Attr_1_4_NoCapture - = Attr_1_NoCapture.addAttribute(gIR->context(), 4, llvm::Attribute::NoCapture); - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // void _d_assert(string file, uint line) - // void _d_arraybounds(string file, uint line) - { - llvm::StringRef fname ("_d_assert"); - llvm::StringRef fname2("_d_arraybounds"); - LLType *types[] = { stringTy, intTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - } - - // void _d_assert_msg(string msg, string file, uint line) - { - llvm::StringRef fname("_d_assert_msg"); - LLType *types[] = { stringTy, stringTy, intTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // void _d_assertm(immutable(ModuleInfo)* m, uint line) - // void _d_array_bounds(immutable(ModuleInfo)* m, uint line) - { - llvm::StringRef fname ("_d_assertm"); - llvm::StringRef fname2("_d_array_bounds"); - LLType *types[] = { moduleInfoPtrTy, intTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - } - - // void _d_switch_error(immutable(ModuleInfo)* m, uint line) - { - llvm::StringRef fname("_d_switch_error"); - LLType *types[] = { moduleInfoPtrTy, intTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - - // void* _d_allocmemory(size_t sz) - { - llvm::StringRef fname("_d_allocmemory"); - LLType *types[] = { sizeTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_NoAlias); - } - - // void* _d_allocmemoryT(TypeInfo ti) - { - llvm::StringRef fname("_d_allocmemoryT"); - LLType *types[] = { typeInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_NoAlias); - } - // void[] _d_newarrayT (const TypeInfo ti, size_t length) - // void[] _d_newarrayiT(const TypeInfo ti, size_t length) - // void[] _d_newarrayU (const TypeInfo ti, size_t length) - { - llvm::StringRef fname ("_d_newarrayT"); - llvm::StringRef fname2("_d_newarrayiT"); - llvm::StringRef fname3("_d_newarrayU"); - LLType *types[] = { typeInfoTy, sizeTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname3, M); - } - // void[] _d_newarraymTX (const TypeInfo ti, size_t[] dims) - // void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) - { - llvm::StringRef fname ("_d_newarraymTX"); - llvm::StringRef fname2("_d_newarraymiTX"); - LLType *types[] = { typeInfoTy, rt_array(sizeTy) }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - } - - // void[] _d_arraysetlengthT (const TypeInfo ti, size_t newlength, void[]* p) - // void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) - { - llvm::StringRef fname ("_d_arraysetlengthT"); - llvm::StringRef fname2("_d_arraysetlengthiT"); - LLType *types[] = { typeInfoTy, sizeTy, voidArrayPtrTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - } - - // byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) - { - llvm::StringRef fname("_d_arrayappendcTX"); - LLType *types[] = { typeInfoTy, voidArrayPtrTy, sizeTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - // void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y) - { - llvm::StringRef fname("_d_arrayappendT"); - LLType *types[] = { typeInfoTy, voidArrayPtrTy, voidArrayTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - // void[] _d_arrayappendcd(ref byte[] x, dchar c) - { - llvm::StringRef fname("_d_arrayappendcd"); - LLType *types[] = { voidArrayPtrTy, intTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - // void[] _d_arrayappendwd(ref byte[] x, dchar c) - { - llvm::StringRef fname("_d_arrayappendwd"); - LLType *types[] = { voidArrayPtrTy, intTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - // byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) - { - llvm::StringRef fname("_d_arraycatT"); - LLType *types[] = { typeInfoTy, voidArrayTy, voidArrayTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - // void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs) - { - llvm::StringRef fname("_d_arraycatnTX"); - LLType* types[] = { typeInfoTy, rt_array(voidArrayTy) }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // Object _d_newclass(const ClassInfo ci) - { - llvm::StringRef fname("_d_newclass"); - LLType *types[] = { classInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(objectTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_NoAlias); - } - - // void* _d_newitemT (TypeInfo ti) - // void* _d_newitemiT(TypeInfo ti) - { - llvm::StringRef fname ("_d_newitemT"); - llvm::StringRef fname2("_d_newitemiT"); - LLType *types[] = { typeInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_NoAlias); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M) - ->setAttributes(Attr_NoAlias); - } - - // void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) - { - llvm::StringRef fname("_d_delarray_t"); - LLType *types[] = { voidArrayPtrTy, DtoType(Type::typeinfostruct->type) }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // void _d_delmemory(void** p) - // void _d_delinterface(void** p) - { - llvm::StringRef fname("_d_delmemory"); - llvm::StringRef fname2("_d_delinterface"); - LLType *types[] = { rt_ptr(voidPtrTy) }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - } - - // void _d_callfinalizer(void* p) - { - llvm::StringRef fname("_d_callfinalizer"); - LLType *types[] = { voidPtrTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // D2: void _d_delclass(Object* p) - { - llvm::StringRef fname("_d_delclass"); - LLType *types[] = { rt_ptr(objectTy) }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // void _d_delstruct(void** p, TypeInfo_Struct inf) - { - llvm::StringRef fname("_d_delstruct"); - LLType *types[] = { rt_ptr(voidPtrTy), DtoType(Type::typeinfostruct->type) }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // array slice copy when assertions are on! - // void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t srclen) - { - llvm::StringRef fname("_d_array_slice_copy"); - LLType *types[] = { voidPtrTy, sizeTy, voidPtrTy, sizeTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_1_3_NoCapture); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // int _aApplycd1(in char[] aa, dg_t dg) - // int _aApplyRcd1(in char[] aa, dg_t dg) - #define STR_APPLY1(TY,a,b) \ - { \ - const std::string prefix = "_aApply"; \ - std::string fname1 = prefix + a + '1', \ - fname2 = prefix + b + '1', \ - fname3 = prefix + 'R' + a + '1', \ - fname4 = prefix + 'R' + b + '1'; \ - LLType *types[] = { TY, rt_dg1() }; \ - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); \ - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname1, M); \ - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); \ - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname3, M); \ - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname4, M); \ - } - STR_APPLY1(stringTy, "cw", "cd") - STR_APPLY1(wstringTy, "wc", "wd") - STR_APPLY1(dstringTy, "dc", "dw") - #undef STR_APPLY1 - - // int _aApplycd2(in char[] aa, dg2_t dg) - // int _aApplyRcd2(in char[] aa, dg2_t dg) - #define STR_APPLY2(TY,a,b) \ - { \ - const std::string prefix = "_aApply"; \ - std::string fname1 = prefix + a + '2', \ - fname2 = prefix + b + '2', \ - fname3 = prefix + 'R' + a + '2', \ - fname4 = prefix + 'R' + b + '2'; \ - LLType *types[] = { TY, rt_dg2() }; \ - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); \ - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname1, M); \ - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); \ - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname3, M); \ - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname4, M); \ - } - STR_APPLY2(stringTy, "cw", "cd") - STR_APPLY2(wstringTy, "wc", "wd") - STR_APPLY2(dstringTy, "dc", "dw") - #undef STR_APPLY2 - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // fixes the length for dynamic array casts - // size_t _d_array_cast_len(size_t len, size_t elemsz, size_t newelemsz) - { - llvm::StringRef fname("_d_array_cast_len"); - LLType *types[] = { sizeTy, sizeTy, sizeTy }; - LLFunctionType* fty = llvm::FunctionType::get(sizeTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadNone); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp) - // void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp) - { - llvm::StringRef fname("_d_arrayassign_l"); - llvm::StringRef fname2("_d_arrayassign_r"); - LLType *types[] = { typeInfoTy, voidArrayTy, voidArrayTy, voidPtrTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - } - - // void[] _d_arrayctor(TypeInfo ti, void[] from, void[] to) - { - llvm::StringRef fname("_d_arrayctor"); - LLType *types[] = { typeInfoTy, voidArrayTy, voidArrayTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti) - // void* _d_arraysetctor(void* p, void* value, int count, TypeInfo ti) - { - llvm::StringRef fname("_d_arraysetassign"); - llvm::StringRef fname2("_d_arraysetctor"); - LLType *types[] = { voidPtrTy, voidPtrTy, intTy, typeInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_NoAlias); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M) - ->setAttributes(Attr_NoAlias); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // cast interface - // void* _d_interface_cast(void* p, ClassInfo c) - { - llvm::StringRef fname("_d_interface_cast"); - LLType *types[] = { voidPtrTy, classInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadOnly_NoUnwind); - } - - // dynamic cast - // void* _d_dynamic_cast(Object o, ClassInfo c) - { - llvm::StringRef fname("_d_dynamic_cast"); - LLType *types[] = { objectTy, classInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadOnly_NoUnwind); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // char[] _adReverseChar(char[] a) - // char[] _adSortChar(char[] a) - { - llvm::StringRef fname("_adReverseChar"); - llvm::StringRef fname2("_adSortChar"); - LLType *types[] = { stringTy }; - LLFunctionType* fty = llvm::FunctionType::get(stringTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - } - - // wchar[] _adReverseWchar(wchar[] a) - // wchar[] _adSortWchar(wchar[] a) - { - llvm::StringRef fname("_adReverseWchar"); - llvm::StringRef fname2("_adSortWchar"); - LLType *types[] = { wstringTy }; - LLFunctionType* fty = llvm::FunctionType::get(wstringTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - } - - // void[] _adReverse(void[] a, size_t szelem) - { - llvm::StringRef fname("_adReverse"); - LLType *types[] = { voidArrayTy, sizeTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_NoUnwind); - } - - // int _adEq(void[] a1, void[] a2, TypeInfo ti) - // int _adCmp(void[] a1, void[] a2, TypeInfo ti) - { - llvm::StringRef fname("_adEq2"); - llvm::StringRef fname2("_adCmp2"); - LLType *types[] = { voidArrayTy, voidArrayTy, typeInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadOnly); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M) - ->setAttributes(Attr_ReadOnly); - } - - // int _adCmpChar(void[] a1, void[] a2) - { - llvm::StringRef fname("_adCmpChar"); - LLType *types[] = { voidArrayTy, voidArrayTy }; - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadOnly_NoUnwind); - } - - // void[] _adSort(void[] a, TypeInfo ti) - { - llvm::StringRef fname("_adSort"); - LLType *types[] = { voidArrayTy, typeInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // size_t _aaLen(in AA aa) - { - llvm::StringRef fname("_aaLen"); - LLType *types[] = { aaTy }; - LLFunctionType* fty = llvm::FunctionType::get(sizeTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadOnly_NoUnwind_1_NoCapture); - } - - // void* _aaGetY(AA* aa, const TypeInfo aati, in size_t valuesize, in void* pkey) - { - llvm::StringRef fname("_aaGetY"); - LLType *types[] = { rt_ptr(aaTy), aaTypeInfoTy, sizeTy, voidPtrTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_1_4_NoCapture); - } - - // inout(void)* _aaInX(inout AA aa, in TypeInfo keyti, in void* pkey) - { - llvm::StringRef fname("_aaInX"); - LLType *types[] = { aaTy, typeInfoTy, voidPtrTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadOnly_1_3_NoCapture); - } - - // bool _aaDelX(AA aa, in TypeInfo keyti, in void* pkey) - { - llvm::StringRef fname("_aaDelX"); - LLType *types[] = { aaTy, typeInfoTy, voidPtrTy }; - LLFunctionType* fty = llvm::FunctionType::get(boolTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_1_3_NoCapture); - } - - // inout(void[]) _aaValues(inout AA aa, in size_t keysize, in size_t valuesize, const TypeInfo tiValueArray) - { - llvm::StringRef fname("_aaValues"); - LLType *types[] = { aaTy, sizeTy, sizeTy, typeInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_NoAlias_1_NoCapture); - } - - // void* _aaRehash(AA* paa, in TypeInfo keyti) - { - llvm::StringRef fname("_aaRehash"); - LLType *types[] = { rt_ptr(aaTy), typeInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // inout(void[]) _aaKeys(inout AA aa, in size_t keysize, const TypeInfo tiKeyArray) - { - llvm::StringRef fname("_aaKeys"); - LLType *types[] = { aaTy, sizeTy, typeInfoTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidArrayTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_NoAlias_1_NoCapture); - } - - // int _aaApply(AA aa, in size_t keysize, dg_t dg) - { - llvm::StringRef fname("_aaApply"); - LLType *types[] = { aaTy, sizeTy, rt_dg1() }; - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_1_NoCapture); - } - - // int _aaApply2(AA aa, in size_t keysize, dg2_t dg) - { - llvm::StringRef fname("_aaApply2"); - LLType *types[] = { aaTy, sizeTy, rt_dg2() }; - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_1_NoCapture); - } - - // int _aaEqual(in TypeInfo tiRaw, in AA e1, in AA e2) - { - llvm::StringRef fname("_aaEqual"); - LLType *types[] = { typeInfoTy, aaTy, aaTy }; - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_1_2_NoCapture); - } - // AA _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values) - { - llvm::StringRef fname("_d_assocarrayliteralTX"); - LLType *types[] = { aaTypeInfoTy, voidArrayTy, voidArrayTy }; - LLFunctionType* fty = llvm::FunctionType::get(aaTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // void _moduleCtor() - // void _moduleDtor() - { - llvm::StringRef fname("_moduleCtor"); - llvm::StringRef fname2("_moduleDtor"); - LLFunctionType* fty = llvm::FunctionType::get(voidTy, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // void _d_throw_exception(Object e) - { - llvm::StringRef fname("_d_throw_exception"); - LLType *types[] = { objectTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // int _d_switch_string(char[][] table, char[] ca) - { - llvm::StringRef fname("_d_switch_string"); - LLType *types[] = { rt_array(stringTy), stringTy }; - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadOnly); - } - - // int _d_switch_ustring(wchar[][] table, wchar[] ca) - { - llvm::StringRef fname("_d_switch_ustring"); - LLType *types[] = { rt_array(wstringTy), wstringTy }; - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadOnly); - } - - // int _d_switch_dstring(dchar[][] table, dchar[] ca) - { - llvm::StringRef fname("_d_switch_dstring"); - LLType *types[] = { rt_array(dstringTy), dstringTy }; - LLFunctionType* fty = llvm::FunctionType::get(intTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) - ->setAttributes(Attr_ReadOnly); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // int _d_eh_personality(...) - { - LLFunctionType* fty = NULL; - if (global.params.targetTriple.isWindowsMSVCEnvironment()) - { - // int _d_eh_personality(ptr ExceptionRecord, ptr EstablisherFrame, ptr ContextRecord, ptr DispatcherContext) - LLType *types[] = { voidPtrTy, voidPtrTy, voidPtrTy, voidPtrTy }; - fty = llvm::FunctionType::get(intTy, types, false); - } - else if (global.params.targetTriple.getArch() == llvm::Triple::arm) - { - // int _d_eh_personality(int state, ptr ucb, ptr context) - LLType *types[] = { intTy, voidPtrTy, voidPtrTy }; - fty = llvm::FunctionType::get(intTy, types, false); - } - else - { - // int _d_eh_personality(int ver, int actions, ulong eh_class, ptr eh_info, ptr context) - LLType *types[] = { intTy, intTy, longTy, voidPtrTy, voidPtrTy }; - fty = llvm::FunctionType::get(intTy, types, false); - } - - llvm::StringRef fname("_d_eh_personality"); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // void _d_eh_resume_unwind(ptr exc_struct) - { - llvm::StringRef fname("_d_eh_resume_unwind"); - LLType *types[] = { voidPtrTy }; - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // void _d_eh_enter_catch() - { - llvm::StringRef fname("_d_eh_enter_catch"); - LLFunctionType* fty = llvm::FunctionType::get(voidTy, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M)-> - setAttributes(Attr_NoUnwind); - } - - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// - - // void invariant._d_invariant(Object o) - { - // KLUDGE: _d_invariant is actually extern(D) in the upstream runtime, possibly - // for more efficient parameter passing on x86. This complicates our code here - // quite a bit, though. - Parameters* params = new Parameters(); - params->push(new Parameter(STCin, ClassDeclaration::object->type, NULL, NULL)); - TypeFunction* dty = new TypeFunction(params, Type::tvoid, 0, LINKd); - llvm::Function* fn = llvm::Function::Create( - llvm::cast(DtoType(dty)), - llvm::GlobalValue::ExternalLinkage, - gABI->mangleForLLVM("_D9invariant12_d_invariantFC6ObjectZv", LINKd), - M - ); - assert(dty->ctype); - IrFuncTy &irFty = dty->ctype->getIrFuncTy(); - gABI->rewriteFunctionType(dty, irFty); - fn->addAttributes(1, llvm::AttributeSet::get(gIR->context(), 1, irFty.args[0]->attrs)); - fn->setCallingConv(gABI->callingConv(fn->getFunctionType(), LINKd)); - } - - // void _d_dso_registry(CompilerDSOData* data) - if (global.params.isLinux) { - llvm::StringRef fname("_d_dso_registry"); - - llvm::StructType* dsoDataTy = llvm::StructType::get( - sizeTy, // version - rt_ptr(voidPtrTy), // slot - rt_ptr(moduleInfoPtrTy), // _minfo_beg - rt_ptr(moduleInfoPtrTy), // _minfo_end - NULL - ); - - llvm::Type* types[] = { rt_ptr(dsoDataTy) }; - llvm::FunctionType* fty = llvm::FunctionType::get(voidTy, types, false); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); - } - - // extern (C) void _d_cover_register2(string filename, size_t[] valid, uint[] data, ubyte minPercent) - // as defined in druntime/rt/cover.d. - if (global.params.cov) { - llvm::StringRef fname("_d_cover_register2"); - - LLType* params[] = { - stringTy, - rt_array(sizeTy), - rt_array(intTy), - byteTy - }; - - LLFunctionType* fty = LLFunctionType::get(voidTy, params, false); - llvm::Function* fn = LLFunction::Create(fty, LLGlobalValue::ExternalLinkage, fname, M); - fn->setCallingConv(gABI->callingConv(fn->getFunctionType(), LINKc)); - } + LLFunctionType *fnty = fn->getFunctionType(); + LLFunction *resfn = + llvm::cast(target.getOrInsertFunction(name, fnty)); + resfn->setAttributes(fn->getAttributes()); + resfn->setCallingConv(fn->getCallingConv()); + return resfn; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +llvm::GlobalVariable *LLVM_D_GetRuntimeGlobal(Loc &loc, llvm::Module &target, + const char *name) { + LLGlobalVariable *gv = target.getNamedGlobal(name); + if (gv) { + return gv; + } + + checkForImplicitGCCall(loc, name); + + if (!M) { + LLVM_D_InitRuntime(); + } + + LLGlobalVariable *g = M->getNamedGlobal(name); + if (!g) { + error(loc, "Runtime global '%s' was not found", name); + fatal(); + // return NULL; + } + + LLPointerType *t = g->getType(); + return getOrCreateGlobal(loc, target, t->getElementType(), g->isConstant(), + g->getLinkage(), NULL, g->getName()); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +static LLType *rt_ptr(LLType *t) { return getPtrToType(t); } + +static LLType *rt_array(LLType *elemty) { + LLType *types[] = {DtoSize_t(), rt_ptr(elemty)}; + return LLStructType::get(gIR->context(), types, false); +} + +static LLType *rt_dg1() { + LLType *types1[] = {rt_ptr(LLType::getInt8Ty(gIR->context())), + rt_ptr(LLType::getInt8Ty(gIR->context()))}; + LLFunctionType *fty = + LLFunctionType::get(LLType::getInt32Ty(gIR->context()), types1, false); + + LLType *types[] = {rt_ptr(LLType::getInt8Ty(gIR->context())), rt_ptr(fty)}; + return LLStructType::get(gIR->context(), types, false); +} + +static LLType *rt_dg2() { + LLType *Int8PtrTy = rt_ptr(LLType::getInt8Ty(gIR->context())); + LLType *types1[] = {Int8PtrTy, Int8PtrTy, Int8PtrTy}; + LLFunctionType *fty = + LLFunctionType::get(LLType::getInt32Ty(gIR->context()), types1, false); + + LLType *types[] = {rt_ptr(LLType::getInt8Ty(gIR->context())), rt_ptr(fty)}; + return LLStructType::get(gIR->context(), types, false); +} + +template static void ensureDecl(DECL *decl, const char *msg) { + if (!decl || !decl->type) { + Logger::println("Missing class declaration: %s\n", msg); + error(Loc(), "Missing class declaration: %s", msg); + errorSupplemental(Loc(), + "Please check that object.di is included and valid"); + fatal(); + } +} + +static void LLVM_D_BuildRuntimeModule() { + Logger::println("building runtime module"); + M = new llvm::Module("ldc internal runtime", gIR->context()); + + LLType *voidTy = LLType::getVoidTy(gIR->context()); + LLType *boolTy = LLType::getInt1Ty(gIR->context()); + LLType *byteTy = LLType::getInt8Ty(gIR->context()); + LLType *intTy = LLType::getInt32Ty(gIR->context()); + LLType *longTy = LLType::getInt64Ty(gIR->context()); + LLType *sizeTy = DtoSize_t(); + + LLType *voidPtrTy = rt_ptr(byteTy); + LLType *voidArrayTy = rt_array(byteTy); + LLType *voidArrayPtrTy = rt_ptr(voidArrayTy); + LLType *stringTy = DtoType(Type::tchar->arrayOf()); + LLType *wstringTy = DtoType(Type::twchar->arrayOf()); + LLType *dstringTy = DtoType(Type::tdchar->arrayOf()); + + // Ensure that the declarations exist before creating llvm types for them. + ensureDecl(ClassDeclaration::object, "Object"); + ensureDecl(Type::typeinfoclass, "TypeInfo_Class"); + ensureDecl(Type::dtypeinfo, "DTypeInfo"); + ensureDecl(Type::typeinfoassociativearray, "TypeInfo_AssociativeArray"); + ensureDecl(Module::moduleinfo, "ModuleInfo"); + + LLType *objectTy = DtoType(ClassDeclaration::object->type); + LLType *classInfoTy = DtoType(Type::typeinfoclass->type); + LLType *typeInfoTy = DtoType(Type::dtypeinfo->type); + LLType *aaTypeInfoTy = DtoType(Type::typeinfoassociativearray->type); + LLType *moduleInfoPtrTy = rt_ptr(DtoType(Module::moduleinfo->type)); + LLType *aaTy = voidPtrTy; + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // Construct some attribute lists used below (possibly multiple times) + llvm::AttributeSet NoAttrs, + Attr_NoAlias = + NoAttrs.addAttribute(gIR->context(), 0, llvm::Attribute::NoAlias), + Attr_NoUnwind = + NoAttrs.addAttribute(gIR->context(), ~0U, llvm::Attribute::NoUnwind), + Attr_ReadOnly = + NoAttrs.addAttribute(gIR->context(), ~0U, llvm::Attribute::ReadOnly), + Attr_ReadOnly_NoUnwind = Attr_ReadOnly.addAttribute( + gIR->context(), ~0U, llvm::Attribute::NoUnwind), + Attr_ReadOnly_1_NoCapture = Attr_ReadOnly.addAttribute( + gIR->context(), 1, llvm::Attribute::NoCapture), + Attr_ReadOnly_1_3_NoCapture = Attr_ReadOnly_1_NoCapture.addAttribute( + gIR->context(), 3, llvm::Attribute::NoCapture), + Attr_ReadOnly_NoUnwind_1_NoCapture = + Attr_ReadOnly_1_NoCapture.addAttribute(gIR->context(), ~0U, + llvm::Attribute::NoUnwind), + Attr_ReadNone = + NoAttrs.addAttribute(gIR->context(), ~0U, llvm::Attribute::ReadNone), + Attr_1_NoCapture = + NoAttrs.addAttribute(gIR->context(), 1, llvm::Attribute::NoCapture), + Attr_NoAlias_1_NoCapture = Attr_1_NoCapture.addAttribute( + gIR->context(), 0, llvm::Attribute::NoAlias), + Attr_1_2_NoCapture = Attr_1_NoCapture.addAttribute( + gIR->context(), 2, llvm::Attribute::NoCapture), + Attr_1_3_NoCapture = Attr_1_NoCapture.addAttribute( + gIR->context(), 3, llvm::Attribute::NoCapture), + Attr_1_4_NoCapture = Attr_1_NoCapture.addAttribute( + gIR->context(), 4, llvm::Attribute::NoCapture); + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // void _d_assert(string file, uint line) + // void _d_arraybounds(string file, uint line) + { + llvm::StringRef fname("_d_assert"); + llvm::StringRef fname2("_d_arraybounds"); + LLType *types[] = {stringTy, intTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + } + + // void _d_assert_msg(string msg, string file, uint line) + { + llvm::StringRef fname("_d_assert_msg"); + LLType *types[] = {stringTy, stringTy, intTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // void _d_assertm(immutable(ModuleInfo)* m, uint line) + // void _d_array_bounds(immutable(ModuleInfo)* m, uint line) + { + llvm::StringRef fname("_d_assertm"); + llvm::StringRef fname2("_d_array_bounds"); + LLType *types[] = {moduleInfoPtrTy, intTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + } + + // void _d_switch_error(immutable(ModuleInfo)* m, uint line) + { + llvm::StringRef fname("_d_switch_error"); + LLType *types[] = {moduleInfoPtrTy, intTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // void* _d_allocmemory(size_t sz) + { + llvm::StringRef fname("_d_allocmemory"); + LLType *types[] = {sizeTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidPtrTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_NoAlias); + } + + // void* _d_allocmemoryT(TypeInfo ti) + { + llvm::StringRef fname("_d_allocmemoryT"); + LLType *types[] = {typeInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidPtrTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_NoAlias); + } + // void[] _d_newarrayT (const TypeInfo ti, size_t length) + // void[] _d_newarrayiT(const TypeInfo ti, size_t length) + // void[] _d_newarrayU (const TypeInfo ti, size_t length) + { + llvm::StringRef fname("_d_newarrayT"); + llvm::StringRef fname2("_d_newarrayiT"); + llvm::StringRef fname3("_d_newarrayU"); + LLType *types[] = {typeInfoTy, sizeTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname3, M); + } + // void[] _d_newarraymTX (const TypeInfo ti, size_t[] dims) + // void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) + { + llvm::StringRef fname("_d_newarraymTX"); + llvm::StringRef fname2("_d_newarraymiTX"); + LLType *types[] = {typeInfoTy, rt_array(sizeTy)}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + } + + // void[] _d_arraysetlengthT (const TypeInfo ti, size_t newlength, void[]* p) + // void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) + { + llvm::StringRef fname("_d_arraysetlengthT"); + llvm::StringRef fname2("_d_arraysetlengthiT"); + LLType *types[] = {typeInfoTy, sizeTy, voidArrayPtrTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + } + + // byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) + { + llvm::StringRef fname("_d_arrayappendcTX"); + LLType *types[] = {typeInfoTy, voidArrayPtrTy, sizeTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + // void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y) + { + llvm::StringRef fname("_d_arrayappendT"); + LLType *types[] = {typeInfoTy, voidArrayPtrTy, voidArrayTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + // void[] _d_arrayappendcd(ref byte[] x, dchar c) + { + llvm::StringRef fname("_d_arrayappendcd"); + LLType *types[] = {voidArrayPtrTy, intTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + // void[] _d_arrayappendwd(ref byte[] x, dchar c) + { + llvm::StringRef fname("_d_arrayappendwd"); + LLType *types[] = {voidArrayPtrTy, intTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + // byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) + { + llvm::StringRef fname("_d_arraycatT"); + LLType *types[] = {typeInfoTy, voidArrayTy, voidArrayTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + // void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs) + { + llvm::StringRef fname("_d_arraycatnTX"); + LLType *types[] = {typeInfoTy, rt_array(voidArrayTy)}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // Object _d_newclass(const ClassInfo ci) + { + llvm::StringRef fname("_d_newclass"); + LLType *types[] = {classInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(objectTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_NoAlias); + } + + // void* _d_newitemT (TypeInfo ti) + // void* _d_newitemiT(TypeInfo ti) + { + llvm::StringRef fname("_d_newitemT"); + llvm::StringRef fname2("_d_newitemiT"); + LLType *types[] = {typeInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidPtrTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_NoAlias); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M) + ->setAttributes(Attr_NoAlias); + } + + // void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) + { + llvm::StringRef fname("_d_delarray_t"); + LLType *types[] = {voidArrayPtrTy, DtoType(Type::typeinfostruct->type)}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // void _d_delmemory(void** p) + // void _d_delinterface(void** p) + { + llvm::StringRef fname("_d_delmemory"); + llvm::StringRef fname2("_d_delinterface"); + LLType *types[] = {rt_ptr(voidPtrTy)}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + } + + // void _d_callfinalizer(void* p) + { + llvm::StringRef fname("_d_callfinalizer"); + LLType *types[] = {voidPtrTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // D2: void _d_delclass(Object* p) + { + llvm::StringRef fname("_d_delclass"); + LLType *types[] = {rt_ptr(objectTy)}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // void _d_delstruct(void** p, TypeInfo_Struct inf) + { + llvm::StringRef fname("_d_delstruct"); + LLType *types[] = {rt_ptr(voidPtrTy), DtoType(Type::typeinfostruct->type)}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // array slice copy when assertions are on! + // void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t + // srclen) + { + llvm::StringRef fname("_d_array_slice_copy"); + LLType *types[] = {voidPtrTy, sizeTy, voidPtrTy, sizeTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_1_3_NoCapture); + } + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +// int _aApplycd1(in char[] aa, dg_t dg) +// int _aApplyRcd1(in char[] aa, dg_t dg) +#define STR_APPLY1(TY, a, b) \ + { \ + const std::string prefix = "_aApply"; \ + std::string fname1 = prefix + a + '1', fname2 = prefix + b + '1', \ + fname3 = prefix + 'R' + a + '1', \ + fname4 = prefix + 'R' + b + '1'; \ + LLType *types[] = {TY, rt_dg1()}; \ + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); \ + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname1, \ + M); \ + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, \ + M); \ + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname3, \ + M); \ + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname4, \ + M); \ + } + STR_APPLY1(stringTy, "cw", "cd") + STR_APPLY1(wstringTy, "wc", "wd") + STR_APPLY1(dstringTy, "dc", "dw") +#undef STR_APPLY1 + +// int _aApplycd2(in char[] aa, dg2_t dg) +// int _aApplyRcd2(in char[] aa, dg2_t dg) +#define STR_APPLY2(TY, a, b) \ + { \ + const std::string prefix = "_aApply"; \ + std::string fname1 = prefix + a + '2', fname2 = prefix + b + '2', \ + fname3 = prefix + 'R' + a + '2', \ + fname4 = prefix + 'R' + b + '2'; \ + LLType *types[] = {TY, rt_dg2()}; \ + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); \ + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname1, \ + M); \ + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, \ + M); \ + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname3, \ + M); \ + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname4, \ + M); \ + } + STR_APPLY2(stringTy, "cw", "cd") + STR_APPLY2(wstringTy, "wc", "wd") + STR_APPLY2(dstringTy, "dc", "dw") +#undef STR_APPLY2 + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // fixes the length for dynamic array casts + // size_t _d_array_cast_len(size_t len, size_t elemsz, size_t newelemsz) + { + llvm::StringRef fname("_d_array_cast_len"); + LLType *types[] = {sizeTy, sizeTy, sizeTy}; + LLFunctionType *fty = llvm::FunctionType::get(sizeTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadNone); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp) + // void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp) + { + llvm::StringRef fname("_d_arrayassign_l"); + llvm::StringRef fname2("_d_arrayassign_r"); + LLType *types[] = {typeInfoTy, voidArrayTy, voidArrayTy, voidPtrTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + } + + // void[] _d_arrayctor(TypeInfo ti, void[] from, void[] to) + { + llvm::StringRef fname("_d_arrayctor"); + LLType *types[] = {typeInfoTy, voidArrayTy, voidArrayTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti) + // void* _d_arraysetctor(void* p, void* value, int count, TypeInfo ti) + { + llvm::StringRef fname("_d_arraysetassign"); + llvm::StringRef fname2("_d_arraysetctor"); + LLType *types[] = {voidPtrTy, voidPtrTy, intTy, typeInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidPtrTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_NoAlias); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M) + ->setAttributes(Attr_NoAlias); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // cast interface + // void* _d_interface_cast(void* p, ClassInfo c) + { + llvm::StringRef fname("_d_interface_cast"); + LLType *types[] = {voidPtrTy, classInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidPtrTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadOnly_NoUnwind); + } + + // dynamic cast + // void* _d_dynamic_cast(Object o, ClassInfo c) + { + llvm::StringRef fname("_d_dynamic_cast"); + LLType *types[] = {objectTy, classInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidPtrTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadOnly_NoUnwind); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // char[] _adReverseChar(char[] a) + // char[] _adSortChar(char[] a) + { + llvm::StringRef fname("_adReverseChar"); + llvm::StringRef fname2("_adSortChar"); + LLType *types[] = {stringTy}; + LLFunctionType *fty = llvm::FunctionType::get(stringTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + } + + // wchar[] _adReverseWchar(wchar[] a) + // wchar[] _adSortWchar(wchar[] a) + { + llvm::StringRef fname("_adReverseWchar"); + llvm::StringRef fname2("_adSortWchar"); + LLType *types[] = {wstringTy}; + LLFunctionType *fty = llvm::FunctionType::get(wstringTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + } + + // void[] _adReverse(void[] a, size_t szelem) + { + llvm::StringRef fname("_adReverse"); + LLType *types[] = {voidArrayTy, sizeTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_NoUnwind); + } + + // int _adEq(void[] a1, void[] a2, TypeInfo ti) + // int _adCmp(void[] a1, void[] a2, TypeInfo ti) + { + llvm::StringRef fname("_adEq2"); + llvm::StringRef fname2("_adCmp2"); + LLType *types[] = {voidArrayTy, voidArrayTy, typeInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadOnly); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M) + ->setAttributes(Attr_ReadOnly); + } + + // int _adCmpChar(void[] a1, void[] a2) + { + llvm::StringRef fname("_adCmpChar"); + LLType *types[] = {voidArrayTy, voidArrayTy}; + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadOnly_NoUnwind); + } + + // void[] _adSort(void[] a, TypeInfo ti) + { + llvm::StringRef fname("_adSort"); + LLType *types[] = {voidArrayTy, typeInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // size_t _aaLen(in AA aa) + { + llvm::StringRef fname("_aaLen"); + LLType *types[] = {aaTy}; + LLFunctionType *fty = llvm::FunctionType::get(sizeTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadOnly_NoUnwind_1_NoCapture); + } + + // void* _aaGetY(AA* aa, const TypeInfo aati, in size_t valuesize, in void* + // pkey) + { + llvm::StringRef fname("_aaGetY"); + LLType *types[] = {rt_ptr(aaTy), aaTypeInfoTy, sizeTy, voidPtrTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidPtrTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_1_4_NoCapture); + } + + // inout(void)* _aaInX(inout AA aa, in TypeInfo keyti, in void* pkey) + { + llvm::StringRef fname("_aaInX"); + LLType *types[] = {aaTy, typeInfoTy, voidPtrTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidPtrTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadOnly_1_3_NoCapture); + } + + // bool _aaDelX(AA aa, in TypeInfo keyti, in void* pkey) + { + llvm::StringRef fname("_aaDelX"); + LLType *types[] = {aaTy, typeInfoTy, voidPtrTy}; + LLFunctionType *fty = llvm::FunctionType::get(boolTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_1_3_NoCapture); + } + + // inout(void[]) _aaValues(inout AA aa, in size_t keysize, in size_t + // valuesize, const TypeInfo tiValueArray) + { + llvm::StringRef fname("_aaValues"); + LLType *types[] = {aaTy, sizeTy, sizeTy, typeInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_NoAlias_1_NoCapture); + } + + // void* _aaRehash(AA* paa, in TypeInfo keyti) + { + llvm::StringRef fname("_aaRehash"); + LLType *types[] = {rt_ptr(aaTy), typeInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidPtrTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // inout(void[]) _aaKeys(inout AA aa, in size_t keysize, const TypeInfo + // tiKeyArray) + { + llvm::StringRef fname("_aaKeys"); + LLType *types[] = {aaTy, sizeTy, typeInfoTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidArrayTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_NoAlias_1_NoCapture); + } + + // int _aaApply(AA aa, in size_t keysize, dg_t dg) + { + llvm::StringRef fname("_aaApply"); + LLType *types[] = {aaTy, sizeTy, rt_dg1()}; + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_1_NoCapture); + } + + // int _aaApply2(AA aa, in size_t keysize, dg2_t dg) + { + llvm::StringRef fname("_aaApply2"); + LLType *types[] = {aaTy, sizeTy, rt_dg2()}; + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_1_NoCapture); + } + + // int _aaEqual(in TypeInfo tiRaw, in AA e1, in AA e2) + { + llvm::StringRef fname("_aaEqual"); + LLType *types[] = {typeInfoTy, aaTy, aaTy}; + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_1_2_NoCapture); + } + // AA _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, + // void[] values) + { + llvm::StringRef fname("_d_assocarrayliteralTX"); + LLType *types[] = {aaTypeInfoTy, voidArrayTy, voidArrayTy}; + LLFunctionType *fty = llvm::FunctionType::get(aaTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // void _moduleCtor() + // void _moduleDtor() + { + llvm::StringRef fname("_moduleCtor"); + llvm::StringRef fname2("_moduleDtor"); + LLFunctionType *fty = llvm::FunctionType::get(voidTy, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // void _d_throw_exception(Object e) + { + llvm::StringRef fname("_d_throw_exception"); + LLType *types[] = {objectTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // int _d_switch_string(char[][] table, char[] ca) + { + llvm::StringRef fname("_d_switch_string"); + LLType *types[] = {rt_array(stringTy), stringTy}; + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadOnly); + } + + // int _d_switch_ustring(wchar[][] table, wchar[] ca) + { + llvm::StringRef fname("_d_switch_ustring"); + LLType *types[] = {rt_array(wstringTy), wstringTy}; + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadOnly); + } + + // int _d_switch_dstring(dchar[][] table, dchar[] ca) + { + llvm::StringRef fname("_d_switch_dstring"); + LLType *types[] = {rt_array(dstringTy), dstringTy}; + LLFunctionType *fty = llvm::FunctionType::get(intTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_ReadOnly); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // int _d_eh_personality(...) + { + LLFunctionType *fty = NULL; + if (global.params.targetTriple.isWindowsMSVCEnvironment()) { + // int _d_eh_personality(ptr ExceptionRecord, ptr EstablisherFrame, ptr + // ContextRecord, ptr DispatcherContext) + LLType *types[] = {voidPtrTy, voidPtrTy, voidPtrTy, voidPtrTy}; + fty = llvm::FunctionType::get(intTy, types, false); + } else if (global.params.targetTriple.getArch() == llvm::Triple::arm) { + // int _d_eh_personality(int state, ptr ucb, ptr context) + LLType *types[] = {intTy, voidPtrTy, voidPtrTy}; + fty = llvm::FunctionType::get(intTy, types, false); + } else { + // int _d_eh_personality(int ver, int actions, ulong eh_class, ptr + // eh_info, ptr context) + LLType *types[] = {intTy, intTy, longTy, voidPtrTy, voidPtrTy}; + fty = llvm::FunctionType::get(intTy, types, false); + } + + llvm::StringRef fname("_d_eh_personality"); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // void _d_eh_resume_unwind(ptr exc_struct) + { + llvm::StringRef fname("_d_eh_resume_unwind"); + LLType *types[] = {voidPtrTy}; + LLFunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // void _d_eh_enter_catch() + { + llvm::StringRef fname("_d_eh_enter_catch"); + LLFunctionType *fty = llvm::FunctionType::get(voidTy, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) + ->setAttributes(Attr_NoUnwind); + } + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + // void invariant._d_invariant(Object o) + { + // KLUDGE: _d_invariant is actually extern(D) in the upstream runtime, + // possibly + // for more efficient parameter passing on x86. This complicates our code + // here + // quite a bit, though. + Parameters *params = new Parameters(); + params->push( + new Parameter(STCin, ClassDeclaration::object->type, NULL, NULL)); + TypeFunction *dty = new TypeFunction(params, Type::tvoid, 0, LINKd); + llvm::Function *fn = llvm::Function::Create( + llvm::cast(DtoType(dty)), + llvm::GlobalValue::ExternalLinkage, + gABI->mangleForLLVM("_D9invariant12_d_invariantFC6ObjectZv", LINKd), M); + assert(dty->ctype); + IrFuncTy &irFty = dty->ctype->getIrFuncTy(); + gABI->rewriteFunctionType(dty, irFty); + fn->addAttributes( + 1, llvm::AttributeSet::get(gIR->context(), 1, irFty.args[0]->attrs)); + fn->setCallingConv(gABI->callingConv(fn->getFunctionType(), LINKd)); + } + + // void _d_dso_registry(CompilerDSOData* data) + if (global.params.isLinux) { + llvm::StringRef fname("_d_dso_registry"); + + llvm::StructType *dsoDataTy = + llvm::StructType::get(sizeTy, // version + rt_ptr(voidPtrTy), // slot + rt_ptr(moduleInfoPtrTy), // _minfo_beg + rt_ptr(moduleInfoPtrTy), // _minfo_end + NULL); + + llvm::Type *types[] = {rt_ptr(dsoDataTy)}; + llvm::FunctionType *fty = llvm::FunctionType::get(voidTy, types, false); + llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M); + } + + // extern (C) void _d_cover_register2(string filename, size_t[] valid, uint[] + // data, ubyte minPercent) + // as defined in druntime/rt/cover.d. + if (global.params.cov) { + llvm::StringRef fname("_d_cover_register2"); + + LLType *params[] = {stringTy, rt_array(sizeTy), rt_array(intTy), byteTy}; + + LLFunctionType *fty = LLFunctionType::get(voidTy, params, false); + llvm::Function *fn = + LLFunction::Create(fty, LLGlobalValue::ExternalLinkage, fname, M); + fn->setCallingConv(gABI->callingConv(fn->getFunctionType(), LINKc)); + } } diff --git a/gen/runtime.h b/gen/runtime.h index 5c3716e16d..8e9dc92539 100644 --- a/gen/runtime.h +++ b/gen/runtime.h @@ -14,11 +14,10 @@ #ifndef LDC_GEN_RUNTIME_H #define LDC_GEN_RUNTIME_H -namespace llvm -{ - class Function; - class GlobalVariable; - class Module; +namespace llvm { +class Function; +class GlobalVariable; +class Module; } struct Loc; @@ -28,8 +27,10 @@ struct Loc; bool LLVM_D_InitRuntime(); void LLVM_D_FreeRuntime(); -llvm::Function* LLVM_D_GetRuntimeFunction(const Loc &loc, llvm::Module& target, const char* name); +llvm::Function *LLVM_D_GetRuntimeFunction(const Loc &loc, llvm::Module &target, + const char *name); -llvm::GlobalVariable* LLVM_D_GetRuntimeGlobal(const Loc &loc, llvm::Module& target, const char* name); +llvm::GlobalVariable * +LLVM_D_GetRuntimeGlobal(const Loc &loc, llvm::Module &target, const char *name); #endif // LDC_GEN_RUNTIME_H diff --git a/gen/statements.cpp b/gen/statements.cpp index d9810a0ba8..3b9aaf9727 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -37,1482 +37,1507 @@ ////////////////////////////////////////////////////////////////////////////// // FIXME: Integrate these functions -void AsmStatement_toIR(AsmStatement *stmt, IRState * irs); -void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState* p); +void AsmStatement_toIR(AsmStatement *stmt, IRState *irs); +void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p); ////////////////////////////////////////////////////////////////////////////// // used to build the sorted list of cases -struct Case : RootObject -{ - StringExp* str; - size_t index; +struct Case : RootObject { + StringExp *str; + size_t index; - Case(StringExp* s, size_t i) { - str = s; - index = i; - } + Case(StringExp *s, size_t i) { + str = s; + index = i; + } - int compare(RootObject *obj) { - Case* c2 = static_cast(obj); - return str->compare(c2->str); - } + int compare(RootObject *obj) { + Case *c2 = static_cast(obj); + return str->compare(c2->str); + } }; -static LLValue* call_string_switch_runtime(llvm::Value* table, Expression* e) -{ - Type* dt = e->type->toBasetype(); - Type* dtnext = dt->nextOf()->toBasetype(); - TY ty = dtnext->ty; - const char* fname; - if (ty == Tchar) { - fname = "_d_switch_string"; - } - else if (ty == Twchar) { - fname = "_d_switch_ustring"; - } - else if (ty == Tdchar) { - fname = "_d_switch_dstring"; - } - else { - llvm_unreachable("not char/wchar/dchar"); - } +static LLValue *call_string_switch_runtime(llvm::Value *table, Expression *e) { + Type *dt = e->type->toBasetype(); + Type *dtnext = dt->nextOf()->toBasetype(); + TY ty = dtnext->ty; + const char *fname; + if (ty == Tchar) { + fname = "_d_switch_string"; + } else if (ty == Twchar) { + fname = "_d_switch_ustring"; + } else if (ty == Tdchar) { + fname = "_d_switch_dstring"; + } else { + llvm_unreachable("not char/wchar/dchar"); + } - llvm::Function* fn = LLVM_D_GetRuntimeFunction(e->loc, gIR->module, fname); + llvm::Function *fn = LLVM_D_GetRuntimeFunction(e->loc, gIR->module, fname); - IF_LOG { - Logger::cout() << *table->getType() << '\n'; - Logger::cout() << *fn->getFunctionType()->getParamType(0) << '\n'; - } - assert(table->getType() == fn->getFunctionType()->getParamType(0)); + IF_LOG { + Logger::cout() << *table->getType() << '\n'; + Logger::cout() << *fn->getFunctionType()->getParamType(0) << '\n'; + } + assert(table->getType() == fn->getFunctionType()->getParamType(0)); - DValue* val = toElemDtor(e); - LLValue* llval = val->getRVal(); - assert(llval->getType() == fn->getFunctionType()->getParamType(1)); + DValue *val = toElemDtor(e); + LLValue *llval = val->getRVal(); + assert(llval->getType() == fn->getFunctionType()->getParamType(1)); - LLCallSite call = gIR->CreateCallOrInvoke(fn, table, llval); + LLCallSite call = gIR->CreateCallOrInvoke(fn, table, llval); - return call.getInstruction(); + return call.getInstruction(); } ////////////////////////////////////////////////////////////////////////////// class ToIRVisitor : public Visitor { - IRState *irs; + IRState *irs; + public: + ToIRVisitor(IRState *irs) : irs(irs) {} - ToIRVisitor(IRState *irs) : irs(irs) { } + ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// + // Import all functions from class Visitor + using Visitor::visit; - // Import all functions from class Visitor - using Visitor::visit; + ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// + void visit(CompoundStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("CompoundStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; - void visit(CompoundStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("CompoundStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + for (auto s : *stmt->statements) { + if (s) { + s->accept(this); + } + } + } - for (auto s : *stmt->statements) - { - if (s) { - s->accept(this); - } + ////////////////////////////////////////////////////////////////////////// + + void visit(ReturnStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("ReturnStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + // emit dwarf stop point + irs->DBuilder.EmitStopPoint(stmt->loc); + + emitCoverageLinecountInc(stmt->loc); + + // The LLVM value to return, or null for void returns. + llvm::Value *returnValue = 0; + + // is there a return value expression? + if (stmt->exp || (!stmt->exp && (irs->topfunc() == irs->mainFunc))) { + // if the functions return type is void this means that + // we are returning through a pointer argument + if (irs->topfunc()->getReturnType() == + LLType::getVoidTy(irs->context())) { + // sanity check + IrFunction *f = irs->func(); + assert(getIrFunc(f->decl)->retArg); + + // FIXME: is there ever a case where a sret return needs to be rewritten + // for the ABI? + + // get return pointer + DValue *rvar = new DVarValue(f->type->next, getIrFunc(f->decl)->retArg); + DValue *e = toElemDtor(stmt->exp); + // store return value + if (rvar->getLVal() != e->getRVal()) { + DtoAssign(stmt->loc, rvar, e, TOKblit); } + + // call postblit if necessary + if (!irs->func()->type->isref && + !(f->decl->nrvo_can && f->decl->nrvo_var)) + callPostblit(stmt->loc, stmt->exp, rvar->getLVal()); + } + // the return type is not void, so this is a normal "register" return + else { + if (!stmt->exp && (irs->topfunc() == irs->mainFunc)) { + returnValue = + LLConstant::getNullValue(irs->mainFunc->getReturnType()); + } else { + if (stmt->exp->op == TOKnull) + stmt->exp->type = irs->func()->type->next; + DValue *dval = 0; + // call postblit if necessary + if (!irs->func()->type->isref) { + dval = toElemDtor(stmt->exp); + callPostblit(stmt->loc, stmt->exp, dval->getRVal()); + } else { + Expression *ae = stmt->exp->addressOf(); + dval = toElemDtor(ae); + } + // do abi specific transformations on the return value + returnValue = getIrFunc(irs->func()->decl)->irFty.putRet(dval); + } + + IrFunction *f = irs->func(); + // Hack around LDC assuming structs and static arrays are in memory: + // If the function returns a struct or a static array, and the return + // value is a pointer to a struct or a static array, load from it + // before returning. + int ty = f->type->next->toBasetype()->ty; + if (returnValue->getType() != irs->topfunc()->getReturnType() && + (ty == Tstruct || ty == Tsarray) && + isaPointer(returnValue->getType())) { + Logger::println("Loading value for return"); + returnValue = DtoLoad(returnValue); + } + + // can happen for classes and void main + if (returnValue->getType() != irs->topfunc()->getReturnType()) { + // for the main function this only happens if it is declared as void + // and then contains a return (exp); statement. Since the actual + // return type remains i32, we just throw away the exp value + // and return 0 instead + // if we're not in main, just bitcast + if (irs->topfunc() == irs->mainFunc) + returnValue = + LLConstant::getNullValue(irs->mainFunc->getReturnType()); + else + returnValue = irs->ir->CreateBitCast( + returnValue, irs->topfunc()->getReturnType()); + + IF_LOG Logger::cout() << "return value after cast: " << *returnValue + << '\n'; + } + } + } else { + // no return value expression means it's a void function. + assert(irs->topfunc()->getReturnType() == + LLType::getVoidTy(irs->context())); } - ////////////////////////////////////////////////////////////////////////// - - void visit(ReturnStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("ReturnStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // emit dwarf stop point - irs->DBuilder.EmitStopPoint(stmt->loc); - - emitCoverageLinecountInc(stmt->loc); - - // The LLVM value to return, or null for void returns. - llvm::Value *returnValue = 0; - - // is there a return value expression? - if (stmt->exp || (!stmt->exp && (irs->topfunc() == irs->mainFunc)) ) - { - // if the functions return type is void this means that - // we are returning through a pointer argument - if (irs->topfunc()->getReturnType() == LLType::getVoidTy(irs->context())) - { - // sanity check - IrFunction* f = irs->func(); - assert(getIrFunc(f->decl)->retArg); - - // FIXME: is there ever a case where a sret return needs to be rewritten for the ABI? - - // get return pointer - DValue* rvar = new DVarValue(f->type->next, getIrFunc(f->decl)->retArg); - DValue* e = toElemDtor(stmt->exp); - // store return value - if (rvar->getLVal() != e->getRVal()) - { - DtoAssign(stmt->loc, rvar, e, TOKblit); - } - - // call postblit if necessary - if (!irs->func()->type->isref && !(f->decl->nrvo_can && f->decl->nrvo_var)) - callPostblit(stmt->loc, stmt->exp, rvar->getLVal()); - } - // the return type is not void, so this is a normal "register" return - else - { - if (!stmt->exp && (irs->topfunc() == irs->mainFunc)) { - returnValue = LLConstant::getNullValue(irs->mainFunc->getReturnType()); - } else { - if (stmt->exp->op == TOKnull) - stmt->exp->type = irs->func()->type->next; - DValue* dval = 0; - // call postblit if necessary - if (!irs->func()->type->isref) { - dval = toElemDtor(stmt->exp); - callPostblit(stmt->loc, stmt->exp, dval->getRVal()); - } else { - Expression *ae = stmt->exp->addressOf(); - dval = toElemDtor(ae); - } - // do abi specific transformations on the return value - returnValue = getIrFunc(irs->func()->decl)->irFty.putRet(dval); - } - - IrFunction* f = irs->func(); - // Hack around LDC assuming structs and static arrays are in memory: - // If the function returns a struct or a static array, and the return - // value is a pointer to a struct or a static array, load from it - // before returning. - int ty = f->type->next->toBasetype()->ty; - if (returnValue->getType() != irs->topfunc()->getReturnType() && - (ty == Tstruct - || ty == Tsarray - ) && isaPointer(returnValue->getType())) - { - Logger::println("Loading value for return"); - returnValue = DtoLoad(returnValue); - } - - // can happen for classes and void main - if (returnValue->getType() != irs->topfunc()->getReturnType()) - { - // for the main function this only happens if it is declared as void - // and then contains a return (exp); statement. Since the actual - // return type remains i32, we just throw away the exp value - // and return 0 instead - // if we're not in main, just bitcast - if (irs->topfunc() == irs->mainFunc) - returnValue = LLConstant::getNullValue(irs->mainFunc->getReturnType()); - else - returnValue = irs->ir->CreateBitCast(returnValue, - irs->topfunc()->getReturnType()); - - IF_LOG Logger::cout() << "return value after cast: " << *returnValue << '\n'; - } - } - } - else - { - // no return value expression means it's a void function. - assert(irs->topfunc()->getReturnType() == LLType::getVoidTy(irs->context())); - } - - // If there are no cleanups to run, we try to keep the IR simple and - // just directly emit the return instruction. - - const bool loadFromSlot = irs->func()->scopes->currentCleanupScope() != 0; - if (loadFromSlot) { - const bool retBlockExisted = !!irs->func()->retBlock; - if (!retBlockExisted) { - irs->func()->retBlock = llvm::BasicBlock::Create( - irs->context(), "return", irs->topfunc()); - if (returnValue) { - irs->func()->retValSlot = DtoRawAlloca(returnValue->getType(), - 0, "return.slot"); - } - } - - // Create the store to the slot at the end of our current basic - // block, before we run the cleanups. - if (returnValue) { - irs->ir->CreateStore(returnValue, irs->func()->retValSlot); - } - - // Now run the cleanups. - irs->func()->scopes->runAllCleanups(irs->func()->retBlock); - - // If the return block already exists, we are golden. Otherwise, go - // ahead and emit it now. - if (retBlockExisted) { - return; - } - - irs->scope() = IRScope(irs->func()->retBlock); - } + // If there are no cleanups to run, we try to keep the IR simple and + // just directly emit the return instruction. + const bool loadFromSlot = irs->func()->scopes->currentCleanupScope() != 0; + if (loadFromSlot) { + const bool retBlockExisted = !!irs->func()->retBlock; + if (!retBlockExisted) { + irs->func()->retBlock = + llvm::BasicBlock::Create(irs->context(), "return", irs->topfunc()); if (returnValue) { - // Hack: the frontend generates 'return 0;' as last statement of - // 'void main()'. But the debug location is missing. Use the end - // of function as debug location. - if (irs->func()->decl->isMain() && !stmt->loc.linnum) - irs->DBuilder.EmitStopPoint(irs->func()->decl->endloc); - - irs->ir->CreateRet(loadFromSlot ? - DtoLoad(irs->func()->retValSlot) : returnValue); - } - else - { - irs->ir->CreateRetVoid(); + irs->func()->retValSlot = + DtoRawAlloca(returnValue->getType(), 0, "return.slot"); } + } - // TODO: Should not be needed - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterreturn", irs->topfunc()); - irs->scope() = IRScope(bb); + // Create the store to the slot at the end of our current basic + // block, before we run the cleanups. + if (returnValue) { + irs->ir->CreateStore(returnValue, irs->func()->retValSlot); + } + + // Now run the cleanups. + irs->func()->scopes->runAllCleanups(irs->func()->retBlock); + + // If the return block already exists, we are golden. Otherwise, go + // ahead and emit it now. + if (retBlockExisted) { + return; + } + + irs->scope() = IRScope(irs->func()->retBlock); } - ////////////////////////////////////////////////////////////////////////// + if (returnValue) { + // Hack: the frontend generates 'return 0;' as last statement of + // 'void main()'. But the debug location is missing. Use the end + // of function as debug location. + if (irs->func()->decl->isMain() && !stmt->loc.linnum) + irs->DBuilder.EmitStopPoint(irs->func()->decl->endloc); - void visit(ExpStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("ExpStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // emit dwarf stop point - irs->DBuilder.EmitStopPoint(stmt->loc); - - emitCoverageLinecountInc(stmt->loc); - - if (stmt->exp) { - elem* e; - // a cast(void) around the expression is allowed, but doesn't require any code - if (stmt->exp->op == TOKcast && stmt->exp->type == Type::tvoid) { - CastExp* cexp = static_cast(stmt->exp); - e = toElemDtor(cexp->e1); - } - else - e = toElemDtor(stmt->exp); - delete e; - } + irs->ir->CreateRet(loadFromSlot ? DtoLoad(irs->func()->retValSlot) + : returnValue); + } else { + irs->ir->CreateRetVoid(); } - ////////////////////////////////////////////////////////////////////////// + // TODO: Should not be needed + llvm::BasicBlock *bb = + llvm::BasicBlock::Create(gIR->context(), "afterreturn", irs->topfunc()); + irs->scope() = IRScope(bb); + } - void visit(IfStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("IfStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////// - // start a dwarf lexical block - irs->DBuilder.EmitBlockStart(stmt->loc); - emitCoverageLinecountInc(stmt->loc); + void visit(ExpStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("ExpStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; - if (stmt->match) - DtoRawVarDeclaration(stmt->match); + // emit dwarf stop point + irs->DBuilder.EmitStopPoint(stmt->loc); - DValue* cond_e = toElemDtor(stmt->condition); - LLValue* cond_val = cond_e->getRVal(); + emitCoverageLinecountInc(stmt->loc); - llvm::BasicBlock* ifbb = llvm::BasicBlock::Create(irs->context(), "if", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "endif", irs->topfunc()); - llvm::BasicBlock* elsebb = stmt->elsebody ? llvm::BasicBlock::Create(irs->context(), "else", irs->topfunc(), endbb) : endbb; + if (stmt->exp) { + elem *e; + // a cast(void) around the expression is allowed, but doesn't require any + // code + if (stmt->exp->op == TOKcast && stmt->exp->type == Type::tvoid) { + CastExp *cexp = static_cast(stmt->exp); + e = toElemDtor(cexp->e1); + } else + e = toElemDtor(stmt->exp); + delete e; + } + } - if (cond_val->getType() != LLType::getInt1Ty(irs->context())) { - IF_LOG Logger::cout() << "if conditional: " << *cond_val << '\n'; - cond_val = DtoCast(stmt->loc, cond_e, Type::tbool)->getRVal(); - } - llvm::BranchInst::Create(ifbb, elsebb, cond_val, irs->scopebb()); + ////////////////////////////////////////////////////////////////////////// - // replace current scope - irs->scope() = IRScope(ifbb); + void visit(IfStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("IfStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; - // do scoped statements + // start a dwarf lexical block + irs->DBuilder.EmitBlockStart(stmt->loc); + emitCoverageLinecountInc(stmt->loc); - if (stmt->ifbody) { - irs->DBuilder.EmitBlockStart(stmt->ifbody->loc); - stmt->ifbody->accept(this); - irs->DBuilder.EmitBlockEnd(); - } - if (!irs->scopereturned()) { - llvm::BranchInst::Create(endbb, irs->scopebb()); - } + if (stmt->match) + DtoRawVarDeclaration(stmt->match); - if (stmt->elsebody) { - irs->scope() = IRScope(elsebb); - irs->DBuilder.EmitBlockStart(stmt->elsebody->loc); - stmt->elsebody->accept(this); - if (!irs->scopereturned()) { - llvm::BranchInst::Create(endbb, irs->scopebb()); - } - irs->DBuilder.EmitBlockEnd(); - } + DValue *cond_e = toElemDtor(stmt->condition); + LLValue *cond_val = cond_e->getRVal(); - // end the dwarf lexical block + llvm::BasicBlock *ifbb = + llvm::BasicBlock::Create(irs->context(), "if", irs->topfunc()); + llvm::BasicBlock *endbb = + llvm::BasicBlock::Create(irs->context(), "endif", irs->topfunc()); + llvm::BasicBlock *elsebb = + stmt->elsebody ? llvm::BasicBlock::Create(irs->context(), "else", + irs->topfunc(), endbb) + : endbb; + + if (cond_val->getType() != LLType::getInt1Ty(irs->context())) { + IF_LOG Logger::cout() << "if conditional: " << *cond_val << '\n'; + cond_val = DtoCast(stmt->loc, cond_e, Type::tbool)->getRVal(); + } + llvm::BranchInst::Create(ifbb, elsebb, cond_val, irs->scopebb()); + + // replace current scope + irs->scope() = IRScope(ifbb); + + // do scoped statements + + if (stmt->ifbody) { + irs->DBuilder.EmitBlockStart(stmt->ifbody->loc); + stmt->ifbody->accept(this); + irs->DBuilder.EmitBlockEnd(); + } + if (!irs->scopereturned()) { + llvm::BranchInst::Create(endbb, irs->scopebb()); + } + + if (stmt->elsebody) { + irs->scope() = IRScope(elsebb); + irs->DBuilder.EmitBlockStart(stmt->elsebody->loc); + stmt->elsebody->accept(this); + if (!irs->scopereturned()) { + llvm::BranchInst::Create(endbb, irs->scopebb()); + } + irs->DBuilder.EmitBlockEnd(); + } + + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); + + // rewrite the scope + irs->scope() = IRScope(endbb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(ScopeStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("ScopeStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + if (stmt->statement) { + irs->DBuilder.EmitBlockStart(stmt->statement->loc); + stmt->statement->accept(this); + irs->DBuilder.EmitBlockEnd(); + } + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(WhileStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("WhileStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + // start a dwarf lexical block + irs->DBuilder.EmitBlockStart(stmt->loc); + + // create while blocks + + llvm::BasicBlock *whilebb = + llvm::BasicBlock::Create(irs->context(), "whilecond", irs->topfunc()); + llvm::BasicBlock *whilebodybb = + llvm::BasicBlock::Create(irs->context(), "whilebody", irs->topfunc()); + llvm::BasicBlock *endbb = + llvm::BasicBlock::Create(irs->context(), "endwhile", irs->topfunc()); + + // move into the while block + irs->ir->CreateBr(whilebb); + + // replace current scope + irs->scope() = IRScope(whilebb); + + // create the condition + emitCoverageLinecountInc(stmt->condition->loc); + DValue *cond_e = toElemDtor(stmt->condition); + LLValue *cond_val = DtoCast(stmt->loc, cond_e, Type::tbool)->getRVal(); + delete cond_e; + + // conditional branch + llvm::BranchInst::Create(whilebodybb, endbb, cond_val, irs->scopebb()); + + // rewrite scope + irs->scope() = IRScope(whilebodybb); + + // while body code + irs->func()->scopes->pushLoopTarget(stmt, whilebb, endbb); + if (stmt->body) + stmt->body->accept(this); + irs->func()->scopes->popLoopTarget(); + + // loop + if (!irs->scopereturned()) + llvm::BranchInst::Create(whilebb, irs->scopebb()); + + // rewrite the scope + irs->scope() = IRScope(endbb); + + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(DoStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("DoStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + // start a dwarf lexical block + irs->DBuilder.EmitBlockStart(stmt->loc); + + // create while blocks + llvm::BasicBlock *dowhilebb = + llvm::BasicBlock::Create(irs->context(), "dowhile", irs->topfunc()); + llvm::BasicBlock *condbb = + llvm::BasicBlock::Create(irs->context(), "dowhilecond", irs->topfunc()); + llvm::BasicBlock *endbb = + llvm::BasicBlock::Create(irs->context(), "enddowhile", irs->topfunc()); + + // move into the while block + assert(!irs->scopereturned()); + llvm::BranchInst::Create(dowhilebb, irs->scopebb()); + + // replace current scope + irs->scope() = IRScope(dowhilebb); + + // do-while body code + irs->func()->scopes->pushLoopTarget(stmt, condbb, endbb); + if (stmt->body) + stmt->body->accept(this); + irs->func()->scopes->popLoopTarget(); + + // branch to condition block + llvm::BranchInst::Create(condbb, irs->scopebb()); + irs->scope() = IRScope(condbb); + + // create the condition + emitCoverageLinecountInc(stmt->condition->loc); + DValue *cond_e = toElemDtor(stmt->condition); + LLValue *cond_val = DtoCast(stmt->loc, cond_e, Type::tbool)->getRVal(); + delete cond_e; + + // conditional branch + llvm::BranchInst::Create(dowhilebb, endbb, cond_val, irs->scopebb()); + + // rewrite the scope + irs->scope() = IRScope(endbb); + + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(ForStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("ForStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + // start new dwarf lexical block + irs->DBuilder.EmitBlockStart(stmt->loc); + + // create for blocks + llvm::BasicBlock *forbb = + llvm::BasicBlock::Create(irs->context(), "forcond", irs->topfunc()); + llvm::BasicBlock *forbodybb = + llvm::BasicBlock::Create(irs->context(), "forbody", irs->topfunc()); + llvm::BasicBlock *forincbb = + llvm::BasicBlock::Create(irs->context(), "forinc", irs->topfunc()); + llvm::BasicBlock *endbb = + llvm::BasicBlock::Create(irs->context(), "endfor", irs->topfunc()); + + // init + if (stmt->init != 0) + stmt->init->accept(this); + + // move into the for condition block, ie. start the loop + assert(!irs->scopereturned()); + llvm::BranchInst::Create(forbb, irs->scopebb()); + + // In case of loops that have been rewritten to a composite statement + // containing the initializers and then the actual loop, we need to + // register the former as target scope start. + Statement *scopeStart = stmt->getRelatedLabeled(); + while (ScopeStatement *scope = scopeStart->isScopeStatement()) { + scopeStart = scope->statement; + } + irs->func()->scopes->pushLoopTarget(scopeStart, forincbb, endbb); + + // replace current scope + irs->scope() = IRScope(forbb); + + // create the condition + llvm::Value *cond_val; + if (stmt->condition) { + emitCoverageLinecountInc(stmt->condition->loc); + DValue *cond_e = toElemDtor(stmt->condition); + cond_val = DtoCast(stmt->loc, cond_e, Type::tbool)->getRVal(); + delete cond_e; + } else { + cond_val = DtoConstBool(true); + } + + // conditional branch + assert(!irs->scopereturned()); + llvm::BranchInst::Create(forbodybb, endbb, cond_val, irs->scopebb()); + + // rewrite scope + irs->scope() = IRScope(forbodybb); + + // do for body code + if (stmt->body) + stmt->body->accept(this); + + // move into the for increment block + if (!irs->scopereturned()) + llvm::BranchInst::Create(forincbb, irs->scopebb()); + irs->scope() = IRScope(forincbb); + + // increment + if (stmt->increment) { + emitCoverageLinecountInc(stmt->increment->loc); + DValue *inc = toElemDtor(stmt->increment); + delete inc; + } + + // loop + if (!irs->scopereturned()) + llvm::BranchInst::Create(forbb, irs->scopebb()); + + irs->func()->scopes->popLoopTarget(); + + // rewrite the scope + irs->scope() = IRScope(endbb); + + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(BreakStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("BreakStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + // don't emit two terminators in a row + // happens just before DMD generated default statements if the last case + // terminates + if (irs->scopereturned()) + return; + + // emit dwarf stop point + irs->DBuilder.EmitStopPoint(stmt->loc); + + emitCoverageLinecountInc(stmt->loc); + + if (stmt->ident) { + IF_LOG Logger::println("ident = %s", stmt->ident->toChars()); + + // Get the loop or break statement the label refers to + Statement *targetStatement = stmt->target->statement; + ScopeStatement *tmp; + while ((tmp = targetStatement->isScopeStatement())) + targetStatement = tmp->statement; + + irs->func()->scopes->breakToStatement(targetStatement); + } else { + irs->func()->scopes->breakToClosest(); + } + + // the break terminated this basicblock, start a new one + llvm::BasicBlock *bb = + llvm::BasicBlock::Create(irs->context(), "afterbreak", irs->topfunc()); + irs->scope() = IRScope(bb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(ContinueStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("ContinueStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; + + // emit dwarf stop point + irs->DBuilder.EmitStopPoint(stmt->loc); + + emitCoverageLinecountInc(stmt->loc); + + if (stmt->ident) { + IF_LOG Logger::println("ident = %s", stmt->ident->toChars()); + + // get the loop statement the label refers to + Statement *targetLoopStatement = stmt->target->statement; + ScopeStatement *tmp; + while ((tmp = targetLoopStatement->isScopeStatement())) + targetLoopStatement = tmp->statement; + + irs->func()->scopes->continueWithLoop(targetLoopStatement); + } else { + irs->func()->scopes->continueWithClosest(); + } + + // the break terminated this basicblock, start a new one + llvm::BasicBlock *bb = + llvm::BasicBlock::Create(irs->context(), "afterbreak", irs->topfunc()); + irs->scope() = IRScope(bb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(OnScopeStatement *stmt) LLVM_OVERRIDE { + stmt->error("Internal Compiler Error: OnScopeStatement should have been " + "lowered by frontend."); + fatal(); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(TryFinallyStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("TryFinallyStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; + + // emit dwarf stop point + irs->DBuilder.EmitStopPoint(stmt->loc); + + // We only need to consider exception handling/cleanup issues if there + // is both a try and a finally block. If not, just directly emit what + // is present. + if (!stmt->body || !stmt->finalbody) { + if (stmt->body) { + irs->DBuilder.EmitBlockStart(stmt->body->loc); + stmt->body->accept(this); irs->DBuilder.EmitBlockEnd(); - - // rewrite the scope - irs->scope() = IRScope(endbb); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(ScopeStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("ScopeStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - if (stmt->statement) { - irs->DBuilder.EmitBlockStart(stmt->statement->loc); - stmt->statement->accept(this); - irs->DBuilder.EmitBlockEnd(); - } - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(WhileStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("WhileStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // start a dwarf lexical block - irs->DBuilder.EmitBlockStart(stmt->loc); - - // create while blocks - - llvm::BasicBlock* whilebb = llvm::BasicBlock::Create(irs->context(), "whilecond", irs->topfunc()); - llvm::BasicBlock* whilebodybb = llvm::BasicBlock::Create(irs->context(), "whilebody", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "endwhile", irs->topfunc()); - - // move into the while block - irs->ir->CreateBr(whilebb); - - // replace current scope - irs->scope() = IRScope(whilebb); - - // create the condition - emitCoverageLinecountInc(stmt->condition->loc); - DValue* cond_e = toElemDtor(stmt->condition); - LLValue* cond_val = DtoCast(stmt->loc, cond_e, Type::tbool)->getRVal(); - delete cond_e; - - // conditional branch - llvm::BranchInst::Create(whilebodybb, endbb, cond_val, irs->scopebb()); - - // rewrite scope - irs->scope() = IRScope(whilebodybb); - - // while body code - irs->func()->scopes->pushLoopTarget(stmt, whilebb, endbb); - if (stmt->body) - stmt->body->accept(this); - irs->func()->scopes->popLoopTarget(); - - // loop - if (!irs->scopereturned()) - llvm::BranchInst::Create(whilebb, irs->scopebb()); - - // rewrite the scope - irs->scope() = IRScope(endbb); - - // end the dwarf lexical block - irs->DBuilder.EmitBlockEnd(); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(DoStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("DoStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // start a dwarf lexical block - irs->DBuilder.EmitBlockStart(stmt->loc); - - // create while blocks - llvm::BasicBlock* dowhilebb = llvm::BasicBlock::Create(irs->context(), "dowhile", irs->topfunc()); - llvm::BasicBlock* condbb = llvm::BasicBlock::Create(irs->context(), "dowhilecond", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "enddowhile", irs->topfunc()); - - // move into the while block - assert(!irs->scopereturned()); - llvm::BranchInst::Create(dowhilebb, irs->scopebb()); - - // replace current scope - irs->scope() = IRScope(dowhilebb); - - // do-while body code - irs->func()->scopes->pushLoopTarget(stmt, condbb, endbb); - if (stmt->body) - stmt->body->accept(this); - irs->func()->scopes->popLoopTarget(); - - // branch to condition block - llvm::BranchInst::Create(condbb, irs->scopebb()); - irs->scope() = IRScope(condbb); - - // create the condition - emitCoverageLinecountInc(stmt->condition->loc); - DValue* cond_e = toElemDtor(stmt->condition); - LLValue* cond_val = DtoCast(stmt->loc, cond_e, Type::tbool)->getRVal(); - delete cond_e; - - // conditional branch - llvm::BranchInst::Create(dowhilebb, endbb, cond_val, irs->scopebb()); - - // rewrite the scope - irs->scope() = IRScope(endbb); - - // end the dwarf lexical block - irs->DBuilder.EmitBlockEnd(); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(ForStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("ForStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // start new dwarf lexical block - irs->DBuilder.EmitBlockStart(stmt->loc); - - // create for blocks - llvm::BasicBlock* forbb = llvm::BasicBlock::Create(irs->context(), "forcond", irs->topfunc()); - llvm::BasicBlock* forbodybb = llvm::BasicBlock::Create(irs->context(), "forbody", irs->topfunc()); - llvm::BasicBlock* forincbb = llvm::BasicBlock::Create(irs->context(), "forinc", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "endfor", irs->topfunc()); - - // init - if (stmt->init != 0) - stmt->init->accept(this); - - // move into the for condition block, ie. start the loop - assert(!irs->scopereturned()); - llvm::BranchInst::Create(forbb, irs->scopebb()); - - // In case of loops that have been rewritten to a composite statement - // containing the initializers and then the actual loop, we need to - // register the former as target scope start. - Statement* scopeStart = stmt->getRelatedLabeled(); - while (ScopeStatement* scope = scopeStart->isScopeStatement()) - { - scopeStart = scope->statement; - } - irs->func()->scopes->pushLoopTarget(scopeStart, forincbb, endbb); - - // replace current scope - irs->scope() = IRScope(forbb); - - // create the condition - llvm::Value* cond_val; - if (stmt->condition) - { - emitCoverageLinecountInc(stmt->condition->loc); - DValue* cond_e = toElemDtor(stmt->condition); - cond_val = DtoCast(stmt->loc, cond_e, Type::tbool)->getRVal(); - delete cond_e; - } - else - { - cond_val = DtoConstBool(true); - } - - // conditional branch - assert(!irs->scopereturned()); - llvm::BranchInst::Create(forbodybb, endbb, cond_val, irs->scopebb()); - - // rewrite scope - irs->scope() = IRScope(forbodybb); - - // do for body code - if (stmt->body) - stmt->body->accept(this); - - // move into the for increment block - if (!irs->scopereturned()) - llvm::BranchInst::Create(forincbb, irs->scopebb()); - irs->scope() = IRScope(forincbb); - - // increment - if (stmt->increment) { - emitCoverageLinecountInc(stmt->increment->loc); - DValue* inc = toElemDtor(stmt->increment); - delete inc; - } - - // loop - if (!irs->scopereturned()) - llvm::BranchInst::Create(forbb, irs->scopebb()); - - irs->func()->scopes->popLoopTarget(); - - // rewrite the scope - irs->scope() = IRScope(endbb); - - // end the dwarf lexical block - irs->DBuilder.EmitBlockEnd(); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(BreakStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("BreakStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // don't emit two terminators in a row - // happens just before DMD generated default statements if the last case terminates - if (irs->scopereturned()) - return; - - // emit dwarf stop point - irs->DBuilder.EmitStopPoint(stmt->loc); - - emitCoverageLinecountInc(stmt->loc); - - if (stmt->ident) { - IF_LOG Logger::println("ident = %s", stmt->ident->toChars()); - - // Get the loop or break statement the label refers to - Statement* targetStatement = stmt->target->statement; - ScopeStatement* tmp; - while((tmp = targetStatement->isScopeStatement())) - targetStatement = tmp->statement; - - irs->func()->scopes->breakToStatement(targetStatement); - } else { - irs->func()->scopes->breakToClosest(); - } - - // the break terminated this basicblock, start a new one - llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "afterbreak", irs->topfunc()); - irs->scope() = IRScope(bb); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(ContinueStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("ContinueStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // emit dwarf stop point - irs->DBuilder.EmitStopPoint(stmt->loc); - - emitCoverageLinecountInc(stmt->loc); - - if (stmt->ident) { - IF_LOG Logger::println("ident = %s", stmt->ident->toChars()); - - // get the loop statement the label refers to - Statement* targetLoopStatement = stmt->target->statement; - ScopeStatement* tmp; - while((tmp = targetLoopStatement->isScopeStatement())) - targetLoopStatement = tmp->statement; - - irs->func()->scopes->continueWithLoop(targetLoopStatement); - } else { - irs->func()->scopes->continueWithClosest(); - } - - // the break terminated this basicblock, start a new one - llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "afterbreak", irs->topfunc()); - irs->scope() = IRScope(bb); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(OnScopeStatement *stmt) LLVM_OVERRIDE { - stmt->error("Internal Compiler Error: OnScopeStatement should have been lowered by frontend."); - fatal(); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(TryFinallyStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("TryFinallyStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // emit dwarf stop point - irs->DBuilder.EmitStopPoint(stmt->loc); - - // We only need to consider exception handling/cleanup issues if there - // is both a try and a finally block. If not, just directly emit what - // is present. - if (!stmt->body || !stmt->finalbody) { - if (stmt->body) { - irs->DBuilder.EmitBlockStart(stmt->body->loc); - stmt->body->accept(this); - irs->DBuilder.EmitBlockEnd(); - } else if (stmt->finalbody) { - irs->DBuilder.EmitBlockStart(stmt->finalbody->loc); - stmt->finalbody->accept(this); - irs->DBuilder.EmitBlockEnd(); - } - return; - } - - // We'll append the "try" part to the current basic block later. No need - // for an extra one (we'd need to branch to it unconditionally anyway). - llvm::BasicBlock* trybb = irs->scopebb(); - - // Emit the finally block and set up the cleanup scope for it. - llvm::BasicBlock* finallybb = - llvm::BasicBlock::Create(irs->context(), "finally", irs->topfunc()); - irs->scope() = IRScope(finallybb); + } else if (stmt->finalbody) { irs->DBuilder.EmitBlockStart(stmt->finalbody->loc); stmt->finalbody->accept(this); irs->DBuilder.EmitBlockEnd(); - - CleanupCursor cleanupBefore = irs->func()->scopes->currentCleanupScope(); - irs->func()->scopes->pushCleanup(finallybb, irs->scopebb()); - - // Emit the try block. - irs->scope() = IRScope(trybb); - - assert(stmt->body); - irs->DBuilder.EmitBlockStart(stmt->body->loc); - stmt->body->accept(this); - irs->DBuilder.EmitBlockEnd(); - - // Create a block to branch to after successfully running the try block - // and any cleanups. - if (!irs->scopereturned()) { - llvm::BasicBlock* successbb = llvm::BasicBlock::Create(irs->context(), - "try.success", irs->topfunc()); - irs->func()->scopes->runCleanups(cleanupBefore, successbb); - irs->scope() = IRScope(successbb); - } - irs->func()->scopes->popCleanups(cleanupBefore); + } + return; } - ////////////////////////////////////////////////////////////////////////// + // We'll append the "try" part to the current basic block later. No need + // for an extra one (we'd need to branch to it unconditionally anyway). + llvm::BasicBlock *trybb = irs->scopebb(); - void visit(TryCatchStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("TryCatchStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + // Emit the finally block and set up the cleanup scope for it. + llvm::BasicBlock *finallybb = + llvm::BasicBlock::Create(irs->context(), "finally", irs->topfunc()); + irs->scope() = IRScope(finallybb); + irs->DBuilder.EmitBlockStart(stmt->finalbody->loc); + stmt->finalbody->accept(this); + irs->DBuilder.EmitBlockEnd(); - // Emit dwarf stop point - irs->DBuilder.EmitStopPoint(stmt->loc); + CleanupCursor cleanupBefore = irs->func()->scopes->currentCleanupScope(); + irs->func()->scopes->pushCleanup(finallybb, irs->scopebb()); - // We'll append the "try" part to the current basic block later. No need - // for an extra one (we'd need to branch to it unconditionally anyway). - llvm::BasicBlock* trybb = irs->scopebb(); + // Emit the try block. + irs->scope() = IRScope(trybb); - // Create a basic block to branch to after leaving the try or an - // associated catch block successfully. - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), - "try.success.or.caught", irs->topfunc()); + assert(stmt->body); + irs->DBuilder.EmitBlockStart(stmt->body->loc); + stmt->body->accept(this); + irs->DBuilder.EmitBlockEnd(); - assert(stmt->catches); - - typedef llvm::SmallVector, 6> - CatchBlocks; - CatchBlocks catchBlocks; - catchBlocks.reserve(stmt->catches->dim); - - for (Catches::reverse_iterator it = stmt->catches->rbegin(), - end = stmt->catches->rend(); - it != end; ++it - ) { - llvm::BasicBlock* catchBB = llvm::BasicBlock::Create(irs->context(), - llvm::Twine("catch.") + (*it)->type->toChars(), - irs->topfunc(), endbb); - - irs->scope() = IRScope(catchBB); - irs->DBuilder.EmitBlockStart((*it)->loc); - - llvm::Function* enterCatchFn = - LLVM_D_GetRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); - irs->ir->CreateCall(enterCatchFn); - - // For catches that use the Throwable object, create storage for it. - // We will set it in the code that branches from the landing pads - // (there might be more than one) to catchBB. - if ((*it)->var) { - llvm::Value* ehPtr = irs->func()->getOrCreateEhPtrSlot(); - - if (!global.params.targetTriple.isWindowsMSVCEnvironment()) - { - // ehPtr is a pointer to _d_exception, which has a reference - // to the Throwable object at offset 0. - ehPtr = irs->ir->CreateLoad(ehPtr); - } - - llvm::Type* llCatchVarType = DtoType((*it)->var->type); // e.g., Throwable* - - // Use the same storage for all exceptions that are not accessed in - // nested functions - if (!(*it)->var->nestedrefs.dim) { - assert(!isIrLocalCreated((*it)->var)); - IrLocal* irLocal = getIrLocal((*it)->var, true); - irLocal->value = DtoBitCast(ehPtr, getPtrToType(llCatchVarType)); - } else { - // This will alloca if we haven't already and take care of nested refs - DtoDeclarationExp((*it)->var); - IrLocal* irLocal = getIrLocal((*it)->var); - - // Copy the exception reference over from ehPtr - llvm::Value* exc = DtoLoad(DtoBitCast(ehPtr, llCatchVarType->getPointerTo())); - DtoStore(exc, irLocal->value); - } - } - - // emit handler, if there is one - // handler is zero for instance for 'catch { debug foo(); }' - if ((*it)->handler) { - Statement_toIR((*it)->handler, irs); - } - - if (!irs->scopereturned()) { - irs->ir->CreateBr(endbb); - } - - irs->DBuilder.EmitBlockEnd(); - - catchBlocks.push_back(std::make_pair( - (*it)->type->toBasetype()->isClassHandle(), catchBB)); - } - - // Only after emitting all the catch bodies, register the catch scopes. - // This is so that (re)throwing inside a catch does not match later - // catches. - for (const auto& pair : catchBlocks) - { - DtoResolveClass(pair.first); - irs->func()->scopes->pushCatch( - getIrAggr(pair.first)->getClassInfoSymbol(), pair.second); - } - - // Emit the try block. - irs->scope() = IRScope(trybb); - - assert(stmt->body); - irs->DBuilder.EmitBlockStart(stmt->body->loc); - stmt->body->accept(this); - irs->DBuilder.EmitBlockEnd(); - - if (!irs->scopereturned()) { - llvm::BranchInst::Create(endbb, irs->scopebb()); - } - - // Now that we have done the try block, remove the catches and continue - // codegen in the end block the try and all the catches branch to. - for (size_t i = 0; i < catchBlocks.size(); ++i) { - irs->func()->scopes->popCatch(); - } - - irs->scope() = IRScope(endbb); + // Create a block to branch to after successfully running the try block + // and any cleanups. + if (!irs->scopereturned()) { + llvm::BasicBlock *successbb = llvm::BasicBlock::Create( + irs->context(), "try.success", irs->topfunc()); + irs->func()->scopes->runCleanups(cleanupBefore, successbb); + irs->scope() = IRScope(successbb); } + irs->func()->scopes->popCleanups(cleanupBefore); + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(ThrowStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("ThrowStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + void visit(TryCatchStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("TryCatchStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; - // emit dwarf stop point - irs->DBuilder.EmitStopPoint(stmt->loc); + // Emit dwarf stop point + irs->DBuilder.EmitStopPoint(stmt->loc); - emitCoverageLinecountInc(stmt->loc); + // We'll append the "try" part to the current basic block later. No need + // for an extra one (we'd need to branch to it unconditionally anyway). + llvm::BasicBlock *trybb = irs->scopebb(); - assert(stmt->exp); - DValue* e = toElemDtor(stmt->exp); + // Create a basic block to branch to after leaving the try or an + // associated catch block successfully. + llvm::BasicBlock *endbb = llvm::BasicBlock::Create( + irs->context(), "try.success.or.caught", irs->topfunc()); - llvm::Function* fn = LLVM_D_GetRuntimeFunction(stmt->loc, irs->module, "_d_throw_exception"); - LLValue* arg = DtoBitCast(e->getRVal(), fn->getFunctionType()->getParamType(0));; - irs->CreateCallOrInvoke(fn, arg); - irs->ir->CreateUnreachable(); + assert(stmt->catches); - // TODO: Should not be needed. - llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "afterthrow", irs->topfunc()); - irs->scope() = IRScope(bb); - } + typedef llvm::SmallVector, + 6> CatchBlocks; + CatchBlocks catchBlocks; + catchBlocks.reserve(stmt->catches->dim); - ////////////////////////////////////////////////////////////////////////// + for (Catches::reverse_iterator it = stmt->catches->rbegin(), + end = stmt->catches->rend(); + it != end; ++it) { + llvm::BasicBlock *catchBB = llvm::BasicBlock::Create( + irs->context(), llvm::Twine("catch.") + (*it)->type->toChars(), + irs->topfunc(), endbb); - void visit(SwitchStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("SwitchStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + irs->scope() = IRScope(catchBB); + irs->DBuilder.EmitBlockStart((*it)->loc); - // emit dwarf stop point - irs->DBuilder.EmitStopPoint(stmt->loc); + llvm::Function *enterCatchFn = + LLVM_D_GetRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); + irs->ir->CreateCall(enterCatchFn); - emitCoverageLinecountInc(stmt->loc); + // For catches that use the Throwable object, create storage for it. + // We will set it in the code that branches from the landing pads + // (there might be more than one) to catchBB. + if ((*it)->var) { + llvm::Value *ehPtr = irs->func()->getOrCreateEhPtrSlot(); - llvm::BasicBlock* oldbb = irs->scopebb(); - - // If one of the case expressions is non-constant, we can't use - // 'switch' instruction (that can happen because D2 allows to - // initialize a global variable in a static constructor). - bool useSwitchInst = true; - for (auto cs : *stmt->cases) - { - VarDeclaration* vd = nullptr; - if (cs->exp->op == TOKvar) - vd = static_cast(cs->exp)->var->isVarDeclaration(); - if (vd && (!vd->init || !vd->isConst())) { - cs->llvmIdx = toElemDtor(cs->exp)->getRVal(); - useSwitchInst = false; - } + if (!global.params.targetTriple.isWindowsMSVCEnvironment()) { + // ehPtr is a pointer to _d_exception, which has a reference + // to the Throwable object at offset 0. + ehPtr = irs->ir->CreateLoad(ehPtr); } - // body block. - // FIXME: that block is never used - llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(irs->context(), "switchbody", irs->topfunc()); - - // default - llvm::BasicBlock* defbb = 0; - if (stmt->sdefault) { - Logger::println("has default"); - defbb = llvm::BasicBlock::Create(irs->context(), "default", irs->topfunc()); - stmt->sdefault->bodyBB = defbb; - } - - // end (break point) - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "switchend", irs->topfunc()); - - // do switch body - assert(stmt->body); - irs->scope() = IRScope(bodybb); - irs->func()->scopes->pushBreakTarget(stmt, endbb); - stmt->body->accept(this); - irs->func()->scopes->popBreakTarget(); - if (!irs->scopereturned()) - llvm::BranchInst::Create(endbb, irs->scopebb()); - - irs->scope() = IRScope(oldbb); - if (useSwitchInst) - { - // string switch? - llvm::Value* switchTable = 0; - Objects caseArray; - if (!stmt->condition->type->isintegral()) - { - Logger::println("is string switch"); - // build array of the stringexpS - caseArray.reserve(stmt->cases->dim); - for (unsigned i=0; i < stmt->cases->dim; ++i) - { - CaseStatement* cs = static_cast(stmt->cases->data[i]); - - assert(cs->exp->op == TOKstring); - caseArray.push(new Case(static_cast(cs->exp), i)); - } - // first sort it - caseArray.sort(); - // iterate and add indices to cases - std::vector inits(caseArray.dim, 0); - for (size_t i=0; i < caseArray.dim; ++i) - { - Case* c = static_cast(caseArray.data[i]); - CaseStatement* cs = static_cast(stmt->cases->data[c->index]); - cs->llvmIdx = DtoConstUint(i); - inits[i] = toConstElem(c->str, irs); - } - // build static array for ptr or final array - llvm::Type* elemTy = DtoType(stmt->condition->type); - LLArrayType* arrTy = llvm::ArrayType::get(elemTy, inits.size()); - LLConstant* arrInit = LLConstantArray::get(arrTy, inits); - LLGlobalVariable* arr = new llvm::GlobalVariable(irs->module, arrTy, true, llvm::GlobalValue::InternalLinkage, arrInit, ".string_switch_table_data"); - - LLType* elemPtrTy = getPtrToType(elemTy); - LLConstant* arrPtr = llvm::ConstantExpr::getBitCast(arr, elemPtrTy); - - // build the static table - LLType* types[] = { DtoSize_t(), elemPtrTy }; - LLStructType* sTy = llvm::StructType::get(irs->context(), types, false); - LLConstant* sinits[] = { DtoConstSize_t(inits.size()), arrPtr }; - switchTable = llvm::ConstantStruct::get(sTy, llvm::ArrayRef(sinits)); - } - - // condition var - LLValue* condVal; - // integral switch - if (stmt->condition->type->isintegral()) { - DValue* cond = toElemDtor(stmt->condition); - condVal = cond->getRVal(); - } - // string switch - else { - condVal = call_string_switch_runtime(switchTable, stmt->condition); - } - - // create switch and add the cases - llvm::SwitchInst* si = llvm::SwitchInst::Create(condVal, defbb ? defbb : endbb, stmt->cases->dim, irs->scopebb()); - for (auto cs : *stmt->cases) - si->addCase(isaConstantInt(cs->llvmIdx), cs->bodyBB); - } - else - { // we can't use switch, so we will use a bunch of br instructions instead - DValue* cond = toElemDtor(stmt->condition); - LLValue *condVal = cond->getRVal(); - - llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(irs->context(), "checkcase", irs->topfunc()); - llvm::BranchInst::Create(nextbb, irs->scopebb()); - - irs->scope() = IRScope(nextbb); - for (auto cs : *stmt->cases) - { - LLValue *cmp = irs->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, cs->llvmIdx, condVal, "checkcase"); - nextbb = llvm::BasicBlock::Create(irs->context(), "checkcase", irs->topfunc()); - llvm::BranchInst::Create(cs->bodyBB, nextbb, cmp, irs->scopebb()); - irs->scope() = IRScope(nextbb); - } - - if (stmt->sdefault) { - llvm::BranchInst::Create(stmt->sdefault->bodyBB, irs->scopebb()); - } else { - llvm::BranchInst::Create(endbb, irs->scopebb()); - } - endbb->moveAfter(nextbb); - } - - irs->scope() = IRScope(endbb); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(CaseStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("CaseStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - llvm::BasicBlock* nbb = llvm::BasicBlock::Create(irs->context(), "case", irs->topfunc()); - if (stmt->bodyBB && !stmt->bodyBB->getTerminator()) - { - llvm::BranchInst::Create(nbb, stmt->bodyBB); - } - stmt->bodyBB = nbb; - - if (stmt->llvmIdx == NULL) { - llvm::Constant *c = toConstElem(stmt->exp, irs); - stmt->llvmIdx = isaConstantInt(c); - } - - if (!irs->scopereturned()) - llvm::BranchInst::Create(stmt->bodyBB, irs->scopebb()); - - irs->scope() = IRScope(stmt->bodyBB); - - assert(stmt->statement); - irs->DBuilder.EmitBlockStart(stmt->statement->loc); - emitCoverageLinecountInc(stmt->loc); - stmt->statement->accept(this); - irs->DBuilder.EmitBlockEnd(); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(DefaultStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("DefaultStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - assert(stmt->bodyBB); - - llvm::BasicBlock* nbb = llvm::BasicBlock::Create(irs->context(), "default", irs->topfunc()); - - if (!stmt->bodyBB->getTerminator()) - { - llvm::BranchInst::Create(nbb, stmt->bodyBB); - } - stmt->bodyBB = nbb; - - if (!irs->scopereturned()) - llvm::BranchInst::Create(stmt->bodyBB, irs->scopebb()); - - irs->scope() = IRScope(stmt->bodyBB); - - assert(stmt->statement); - irs->DBuilder.EmitBlockStart(stmt->statement->loc); - emitCoverageLinecountInc(stmt->loc); - stmt->statement->accept(this); - irs->DBuilder.EmitBlockEnd(); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(UnrolledLoopStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("UnrolledLoopStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // if no statements, there's nothing to do - if (!stmt->statements || !stmt->statements->dim) - return; - - // start a dwarf lexical block - irs->DBuilder.EmitBlockStart(stmt->loc); - - // DMD doesn't fold stuff like continue/break, and since this isn't really a loop - // we have to keep track of each statement and jump to the next/end on continue/break - - // create a block for each statement - size_t nstmt = stmt->statements->dim; - llvm::SmallVector blocks(nstmt, NULL); - - for (size_t i=0; i < nstmt; i++) - { - blocks[i] = llvm::BasicBlock::Create(irs->context(), "unrolledstmt", irs->topfunc()); - } - - // create end block - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "unrolledend", irs->topfunc()); - - // enter first stmt - if (!irs->scopereturned()) - irs->ir->CreateBr(blocks[0]); - - // do statements - Statement** stmts = static_cast(stmt->statements->data); - - for (size_t i=0; i < nstmt; i++) - { - Statement *s = stmts[i]; - - // get blocks - llvm::BasicBlock* thisbb = blocks[i]; - llvm::BasicBlock* nextbb = (i+1 == nstmt) ? endbb : blocks[i+1]; - - // update scope - irs->scope() = IRScope(thisbb); - - // push loop scope - // continue goes to next statement, break goes to end - irs->func()->scopes->pushLoopTarget(stmt, nextbb, endbb); - - // do statement - s->accept(this); - - // pop loop scope - irs->func()->scopes->popLoopTarget(); - - // next stmt - if (!irs->scopereturned()) - irs->ir->CreateBr(nextbb); - } - - // finish scope - if (!irs->scopereturned()) - irs->ir->CreateBr(endbb); - irs->scope() = IRScope(endbb); - - // end the dwarf lexical block - irs->DBuilder.EmitBlockEnd(); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(ForeachStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("ForeachStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - // start a dwarf lexical block - irs->DBuilder.EmitBlockStart(stmt->loc); - - //assert(arguments->dim == 1); - assert(stmt->value != 0); - assert(stmt->aggr != 0); - assert(stmt->func != 0); - - //Argument* arg = static_cast(arguments->data[0]); - //Logger::println("Argument is %s", arg->toChars()); - - IF_LOG Logger::println("aggr = %s", stmt->aggr->toChars()); - - // key - LLType* keytype = stmt->key ? DtoType(stmt->key->type) : DtoSize_t(); - LLValue* keyvar; - if (stmt->key) - keyvar = DtoRawVarDeclaration(stmt->key); - else - keyvar = DtoRawAlloca(keytype, 0, "foreachkey"); - LLValue* zerokey = LLConstantInt::get(keytype, 0, false); - - // value - IF_LOG Logger::println("value = %s", stmt->value->toPrettyChars()); - LLValue* valvar = NULL; - if (!stmt->value->isRef() && !stmt->value->isOut()) { - // Create a local variable to serve as the value. - DtoRawVarDeclaration(stmt->value); - valvar = getIrLocal(stmt->value)->value; - } - - // what to iterate - DValue* aggrval = toElemDtor(stmt->aggr); - - // get length and pointer - LLValue* niters = DtoArrayLen(aggrval); - LLValue* val = DtoArrayPtr(aggrval); - - if (niters->getType() != keytype) - { - size_t sz1 = getTypeBitSize(niters->getType()); - size_t sz2 = getTypeBitSize(keytype); - if (sz1 < sz2) - niters = irs->ir->CreateZExt(niters, keytype, "foreachtrunckey"); - else if (sz1 > sz2) - niters = irs->ir->CreateTrunc(niters, keytype, "foreachtrunckey"); - else - niters = irs->ir->CreateBitCast(niters, keytype, "foreachtrunckey"); - } - - if (stmt->op == TOKforeach) { - new llvm::StoreInst(zerokey, keyvar, irs->scopebb()); - } - else { - new llvm::StoreInst(niters, keyvar, irs->scopebb()); - } - - llvm::BasicBlock* condbb = llvm::BasicBlock::Create(irs->context(), "foreachcond", irs->topfunc()); - llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(irs->context(), "foreachbody", irs->topfunc()); - llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(irs->context(), "foreachnext", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "foreachend", irs->topfunc()); - - llvm::BranchInst::Create(condbb, irs->scopebb()); - - // condition - irs->scope() = IRScope(condbb); - - LLValue* done = 0; - LLValue* load = DtoLoad(keyvar); - if (stmt->op == TOKforeach) { - done = irs->ir->CreateICmpULT(load, niters); - } - else if (stmt->op == TOKforeach_reverse) { - done = irs->ir->CreateICmpUGT(load, zerokey); - load = irs->ir->CreateSub(load, LLConstantInt::get(keytype, 1, false)); - DtoStore(load, keyvar); - } - llvm::BranchInst::Create(bodybb, endbb, done, irs->scopebb()); - - // init body - irs->scope() = IRScope(bodybb); - - // get value for this iteration - LLValue* loadedKey = irs->ir->CreateLoad(keyvar); - LLValue* gep = DtoGEP1(val, loadedKey); - - if (!stmt->value->isRef() && !stmt->value->isOut()) { - // Copy value to local variable, and use it as the value variable. - DVarValue dst(stmt->value->type, valvar); - DVarValue src(stmt->value->type, gep); - DtoAssign(stmt->loc, &dst, &src); - getIrLocal(stmt->value)->value = valvar; + llvm::Type *llCatchVarType = + DtoType((*it)->var->type); // e.g., Throwable* + + // Use the same storage for all exceptions that are not accessed in + // nested functions + if (!(*it)->var->nestedrefs.dim) { + assert(!isIrLocalCreated((*it)->var)); + IrLocal *irLocal = getIrLocal((*it)->var, true); + irLocal->value = DtoBitCast(ehPtr, getPtrToType(llCatchVarType)); } else { - // Use the GEP as the address of the value variable. - DtoRawVarDeclaration(stmt->value, gep); + // This will alloca if we haven't already and take care of nested refs + DtoDeclarationExp((*it)->var); + IrLocal *irLocal = getIrLocal((*it)->var); + + // Copy the exception reference over from ehPtr + llvm::Value *exc = + DtoLoad(DtoBitCast(ehPtr, llCatchVarType->getPointerTo())); + DtoStore(exc, irLocal->value); } + } - // emit body - irs->func()->scopes->pushLoopTarget(stmt, nextbb, endbb); - if (stmt->body) - stmt->body->accept(this); - irs->func()->scopes->popLoopTarget(); + // emit handler, if there is one + // handler is zero for instance for 'catch { debug foo(); }' + if ((*it)->handler) { + Statement_toIR((*it)->handler, irs); + } - if (!irs->scopereturned()) - llvm::BranchInst::Create(nextbb, irs->scopebb()); + if (!irs->scopereturned()) { + irs->ir->CreateBr(endbb); + } - // next + irs->DBuilder.EmitBlockEnd(); + + catchBlocks.push_back( + std::make_pair((*it)->type->toBasetype()->isClassHandle(), catchBB)); + } + + // Only after emitting all the catch bodies, register the catch scopes. + // This is so that (re)throwing inside a catch does not match later + // catches. + for (const auto &pair : catchBlocks) { + DtoResolveClass(pair.first); + irs->func()->scopes->pushCatch( + getIrAggr(pair.first)->getClassInfoSymbol(), pair.second); + } + + // Emit the try block. + irs->scope() = IRScope(trybb); + + assert(stmt->body); + irs->DBuilder.EmitBlockStart(stmt->body->loc); + stmt->body->accept(this); + irs->DBuilder.EmitBlockEnd(); + + if (!irs->scopereturned()) { + llvm::BranchInst::Create(endbb, irs->scopebb()); + } + + // Now that we have done the try block, remove the catches and continue + // codegen in the end block the try and all the catches branch to. + for (size_t i = 0; i < catchBlocks.size(); ++i) { + irs->func()->scopes->popCatch(); + } + + irs->scope() = IRScope(endbb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(ThrowStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("ThrowStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + // emit dwarf stop point + irs->DBuilder.EmitStopPoint(stmt->loc); + + emitCoverageLinecountInc(stmt->loc); + + assert(stmt->exp); + DValue *e = toElemDtor(stmt->exp); + + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(stmt->loc, irs->module, "_d_throw_exception"); + LLValue *arg = + DtoBitCast(e->getRVal(), fn->getFunctionType()->getParamType(0)); + ; + irs->CreateCallOrInvoke(fn, arg); + irs->ir->CreateUnreachable(); + + // TODO: Should not be needed. + llvm::BasicBlock *bb = + llvm::BasicBlock::Create(irs->context(), "afterthrow", irs->topfunc()); + irs->scope() = IRScope(bb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(SwitchStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("SwitchStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + // emit dwarf stop point + irs->DBuilder.EmitStopPoint(stmt->loc); + + emitCoverageLinecountInc(stmt->loc); + + llvm::BasicBlock *oldbb = irs->scopebb(); + + // If one of the case expressions is non-constant, we can't use + // 'switch' instruction (that can happen because D2 allows to + // initialize a global variable in a static constructor). + bool useSwitchInst = true; + for (auto cs : *stmt->cases) { + VarDeclaration *vd = nullptr; + if (cs->exp->op == TOKvar) + vd = static_cast(cs->exp)->var->isVarDeclaration(); + if (vd && (!vd->init || !vd->isConst())) { + cs->llvmIdx = toElemDtor(cs->exp)->getRVal(); + useSwitchInst = false; + } + } + + // body block. + // FIXME: that block is never used + llvm::BasicBlock *bodybb = + llvm::BasicBlock::Create(irs->context(), "switchbody", irs->topfunc()); + + // default + llvm::BasicBlock *defbb = 0; + if (stmt->sdefault) { + Logger::println("has default"); + defbb = + llvm::BasicBlock::Create(irs->context(), "default", irs->topfunc()); + stmt->sdefault->bodyBB = defbb; + } + + // end (break point) + llvm::BasicBlock *endbb = + llvm::BasicBlock::Create(irs->context(), "switchend", irs->topfunc()); + + // do switch body + assert(stmt->body); + irs->scope() = IRScope(bodybb); + irs->func()->scopes->pushBreakTarget(stmt, endbb); + stmt->body->accept(this); + irs->func()->scopes->popBreakTarget(); + if (!irs->scopereturned()) + llvm::BranchInst::Create(endbb, irs->scopebb()); + + irs->scope() = IRScope(oldbb); + if (useSwitchInst) { + // string switch? + llvm::Value *switchTable = 0; + Objects caseArray; + if (!stmt->condition->type->isintegral()) { + Logger::println("is string switch"); + // build array of the stringexpS + caseArray.reserve(stmt->cases->dim); + for (unsigned i = 0; i < stmt->cases->dim; ++i) { + CaseStatement *cs = + static_cast(stmt->cases->data[i]); + + assert(cs->exp->op == TOKstring); + caseArray.push(new Case(static_cast(cs->exp), i)); + } + // first sort it + caseArray.sort(); + // iterate and add indices to cases + std::vector inits(caseArray.dim, 0); + for (size_t i = 0; i < caseArray.dim; ++i) { + Case *c = static_cast(caseArray.data[i]); + CaseStatement *cs = + static_cast(stmt->cases->data[c->index]); + cs->llvmIdx = DtoConstUint(i); + inits[i] = toConstElem(c->str, irs); + } + // build static array for ptr or final array + llvm::Type *elemTy = DtoType(stmt->condition->type); + LLArrayType *arrTy = llvm::ArrayType::get(elemTy, inits.size()); + LLConstant *arrInit = LLConstantArray::get(arrTy, inits); + LLGlobalVariable *arr = new llvm::GlobalVariable( + irs->module, arrTy, true, llvm::GlobalValue::InternalLinkage, + arrInit, ".string_switch_table_data"); + + LLType *elemPtrTy = getPtrToType(elemTy); + LLConstant *arrPtr = llvm::ConstantExpr::getBitCast(arr, elemPtrTy); + + // build the static table + LLType *types[] = {DtoSize_t(), elemPtrTy}; + LLStructType *sTy = llvm::StructType::get(irs->context(), types, false); + LLConstant *sinits[] = {DtoConstSize_t(inits.size()), arrPtr}; + switchTable = llvm::ConstantStruct::get( + sTy, llvm::ArrayRef(sinits)); + } + + // condition var + LLValue *condVal; + // integral switch + if (stmt->condition->type->isintegral()) { + DValue *cond = toElemDtor(stmt->condition); + condVal = cond->getRVal(); + } + // string switch + else { + condVal = call_string_switch_runtime(switchTable, stmt->condition); + } + + // create switch and add the cases + llvm::SwitchInst *si = llvm::SwitchInst::Create( + condVal, defbb ? defbb : endbb, stmt->cases->dim, irs->scopebb()); + for (auto cs : *stmt->cases) + si->addCase(isaConstantInt(cs->llvmIdx), cs->bodyBB); + } else { // we can't use switch, so we will use a bunch of br instructions + // instead + DValue *cond = toElemDtor(stmt->condition); + LLValue *condVal = cond->getRVal(); + + llvm::BasicBlock *nextbb = + llvm::BasicBlock::Create(irs->context(), "checkcase", irs->topfunc()); + llvm::BranchInst::Create(nextbb, irs->scopebb()); + + irs->scope() = IRScope(nextbb); + for (auto cs : *stmt->cases) { + LLValue *cmp = irs->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, cs->llvmIdx, + condVal, "checkcase"); + nextbb = llvm::BasicBlock::Create(irs->context(), "checkcase", + irs->topfunc()); + llvm::BranchInst::Create(cs->bodyBB, nextbb, cmp, irs->scopebb()); irs->scope() = IRScope(nextbb); - if (stmt->op == TOKforeach) { - LLValue* load = DtoLoad(keyvar); - load = irs->ir->CreateAdd(load, LLConstantInt::get(keytype, 1, false)); - DtoStore(load, keyvar); - } - llvm::BranchInst::Create(condbb, irs->scopebb()); + } - // end the dwarf lexical block - irs->DBuilder.EmitBlockEnd(); - - // end - irs->scope() = IRScope(endbb); + if (stmt->sdefault) { + llvm::BranchInst::Create(stmt->sdefault->bodyBB, irs->scopebb()); + } else { + llvm::BranchInst::Create(endbb, irs->scopebb()); + } + endbb->moveAfter(nextbb); } - ////////////////////////////////////////////////////////////////////////// + irs->scope() = IRScope(endbb); + } - void visit(ForeachRangeStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("ForeachRangeStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////// - // start a dwarf lexical block - irs->DBuilder.EmitBlockStart(stmt->loc); + void visit(CaseStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("CaseStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; - // evaluate lwr/upr - assert(stmt->lwr->type->isintegral()); - LLValue* lower = toElemDtor(stmt->lwr)->getRVal(); - assert(stmt->upr->type->isintegral()); - LLValue* upper = toElemDtor(stmt->upr)->getRVal(); + llvm::BasicBlock *nbb = + llvm::BasicBlock::Create(irs->context(), "case", irs->topfunc()); + if (stmt->bodyBB && !stmt->bodyBB->getTerminator()) { + llvm::BranchInst::Create(nbb, stmt->bodyBB); + } + stmt->bodyBB = nbb; - // handle key - assert(stmt->key->type->isintegral()); - LLValue* keyval = DtoRawVarDeclaration(stmt->key); - - // store initial value in key - if (stmt->op == TOKforeach) - DtoStore(lower, keyval); - else - DtoStore(upper, keyval); - - // set up the block we'll need - llvm::BasicBlock* condbb = llvm::BasicBlock::Create(irs->context(), "foreachrange_cond", irs->topfunc()); - llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(irs->context(), "foreachrange_body", irs->topfunc()); - llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(irs->context(), "foreachrange_next", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "foreachrange_end", irs->topfunc()); - - // jump to condition - llvm::BranchInst::Create(condbb, irs->scopebb()); - - // CONDITION - irs->scope() = IRScope(condbb); - - // first we test that lwr < upr - lower = DtoLoad(keyval); - assert(lower->getType() == upper->getType()); - llvm::ICmpInst::Predicate cmpop; - if (isLLVMUnsigned(stmt->key->type)) - { - cmpop = (stmt->op == TOKforeach) - ? llvm::ICmpInst::ICMP_ULT - : llvm::ICmpInst::ICMP_UGT; - } - else - { - cmpop = (stmt->op == TOKforeach) - ? llvm::ICmpInst::ICMP_SLT - : llvm::ICmpInst::ICMP_SGT; - } - LLValue* cond = irs->ir->CreateICmp(cmpop, lower, upper); - - // jump to the body if range is ok, to the end if not - llvm::BranchInst::Create(bodybb, endbb, cond, irs->scopebb()); - - // BODY - irs->scope() = IRScope(bodybb); - - // reverse foreach decrements here - if (stmt->op == TOKforeach_reverse) - { - LLValue* v = DtoLoad(keyval); - LLValue* one = LLConstantInt::get(v->getType(), 1, false); - v = irs->ir->CreateSub(v, one); - DtoStore(v, keyval); - } - - // emit body - irs->func()->scopes->pushLoopTarget(stmt, nextbb, endbb); - if (stmt->body) - stmt->body->accept(this); - irs->func()->scopes->popLoopTarget(); - - // jump to next iteration - if (!irs->scopereturned()) - llvm::BranchInst::Create(nextbb, irs->scopebb()); - - // NEXT - irs->scope() = IRScope(nextbb); - - // forward foreach increments here - if (stmt->op == TOKforeach) - { - LLValue* v = DtoLoad(keyval); - LLValue* one = LLConstantInt::get(v->getType(), 1, false); - v = irs->ir->CreateAdd(v, one); - DtoStore(v, keyval); - } - - // jump to condition - llvm::BranchInst::Create(condbb, irs->scopebb()); - - // end the dwarf lexical block - irs->DBuilder.EmitBlockEnd(); - - // END - irs->scope() = IRScope(endbb); + if (stmt->llvmIdx == NULL) { + llvm::Constant *c = toConstElem(stmt->exp, irs); + stmt->llvmIdx = isaConstantInt(c); } - ////////////////////////////////////////////////////////////////////////// + if (!irs->scopereturned()) + llvm::BranchInst::Create(stmt->bodyBB, irs->scopebb()); - void visit(LabelStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("LabelStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + irs->scope() = IRScope(stmt->bodyBB); - // if it's an inline asm label, we don't create a basicblock, just emit it in the asm - if (irs->asmBlock) - { - IRAsmStmt* a = new IRAsmStmt; - std::stringstream label; - printLabelName(label, mangleExact(irs->func()->decl), stmt->ident->toChars()); - label << ":"; - a->code = label.str(); - irs->asmBlock->s.push_back(a); - irs->asmBlock->internalLabels.push_back(stmt->ident); + assert(stmt->statement); + irs->DBuilder.EmitBlockStart(stmt->statement->loc); + emitCoverageLinecountInc(stmt->loc); + stmt->statement->accept(this); + irs->DBuilder.EmitBlockEnd(); + } - // disable inlining - irs->func()->setNeverInline(); - } - else - { - llvm::BasicBlock* labelBB = llvm::BasicBlock::Create(irs->context(), - llvm::Twine("label.") + stmt->ident->toChars(), irs->topfunc()); - irs->func()->scopes->addLabelTarget(stmt->ident, labelBB); + ////////////////////////////////////////////////////////////////////////// - if (!irs->scopereturned()) - llvm::BranchInst::Create(labelBB, irs->scopebb()); + void visit(DefaultStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("DefaultStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; - irs->scope() = IRScope(labelBB); - } + assert(stmt->bodyBB); - if (stmt->statement) { - stmt->statement->accept(this); - } + llvm::BasicBlock *nbb = + llvm::BasicBlock::Create(irs->context(), "default", irs->topfunc()); + + if (!stmt->bodyBB->getTerminator()) { + llvm::BranchInst::Create(nbb, stmt->bodyBB); + } + stmt->bodyBB = nbb; + + if (!irs->scopereturned()) + llvm::BranchInst::Create(stmt->bodyBB, irs->scopebb()); + + irs->scope() = IRScope(stmt->bodyBB); + + assert(stmt->statement); + irs->DBuilder.EmitBlockStart(stmt->statement->loc); + emitCoverageLinecountInc(stmt->loc); + stmt->statement->accept(this); + irs->DBuilder.EmitBlockEnd(); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(UnrolledLoopStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("UnrolledLoopStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; + + // if no statements, there's nothing to do + if (!stmt->statements || !stmt->statements->dim) + return; + + // start a dwarf lexical block + irs->DBuilder.EmitBlockStart(stmt->loc); + + // DMD doesn't fold stuff like continue/break, and since this isn't really a + // loop + // we have to keep track of each statement and jump to the next/end on + // continue/break + + // create a block for each statement + size_t nstmt = stmt->statements->dim; + llvm::SmallVector blocks(nstmt, NULL); + + for (size_t i = 0; i < nstmt; i++) { + blocks[i] = llvm::BasicBlock::Create(irs->context(), "unrolledstmt", + irs->topfunc()); } - ////////////////////////////////////////////////////////////////////////// + // create end block + llvm::BasicBlock *endbb = + llvm::BasicBlock::Create(irs->context(), "unrolledend", irs->topfunc()); - void visit(GotoStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("GotoStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + // enter first stmt + if (!irs->scopereturned()) + irs->ir->CreateBr(blocks[0]); - irs->DBuilder.EmitStopPoint(stmt->loc); + // do statements + Statement **stmts = static_cast(stmt->statements->data); - emitCoverageLinecountInc(stmt->loc); + for (size_t i = 0; i < nstmt; i++) { + Statement *s = stmts[i]; - DtoGoto(stmt->loc, stmt->label); + // get blocks + llvm::BasicBlock *thisbb = blocks[i]; + llvm::BasicBlock *nextbb = (i + 1 == nstmt) ? endbb : blocks[i + 1]; - // TODO: Should not be needed. - llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "aftergoto", irs->topfunc()); - irs->scope() = IRScope(bb); + // update scope + irs->scope() = IRScope(thisbb); + + // push loop scope + // continue goes to next statement, break goes to end + irs->func()->scopes->pushLoopTarget(stmt, nextbb, endbb); + + // do statement + s->accept(this); + + // pop loop scope + irs->func()->scopes->popLoopTarget(); + + // next stmt + if (!irs->scopereturned()) + irs->ir->CreateBr(nextbb); } - ////////////////////////////////////////////////////////////////////////// + // finish scope + if (!irs->scopereturned()) + irs->ir->CreateBr(endbb); + irs->scope() = IRScope(endbb); - void visit(GotoDefaultStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("GotoDefaultStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); + } - irs->DBuilder.EmitStopPoint(stmt->loc); + ////////////////////////////////////////////////////////////////////////// - emitCoverageLinecountInc(stmt->loc); + void visit(ForeachStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("ForeachStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; - assert(!irs->scopereturned()); - assert(stmt->sw->sdefault->bodyBB); + // start a dwarf lexical block + irs->DBuilder.EmitBlockStart(stmt->loc); + + // assert(arguments->dim == 1); + assert(stmt->value != 0); + assert(stmt->aggr != 0); + assert(stmt->func != 0); + + // Argument* arg = static_cast(arguments->data[0]); + // Logger::println("Argument is %s", arg->toChars()); + + IF_LOG Logger::println("aggr = %s", stmt->aggr->toChars()); + + // key + LLType *keytype = stmt->key ? DtoType(stmt->key->type) : DtoSize_t(); + LLValue *keyvar; + if (stmt->key) + keyvar = DtoRawVarDeclaration(stmt->key); + else + keyvar = DtoRawAlloca(keytype, 0, "foreachkey"); + LLValue *zerokey = LLConstantInt::get(keytype, 0, false); + + // value + IF_LOG Logger::println("value = %s", stmt->value->toPrettyChars()); + LLValue *valvar = NULL; + if (!stmt->value->isRef() && !stmt->value->isOut()) { + // Create a local variable to serve as the value. + DtoRawVarDeclaration(stmt->value); + valvar = getIrLocal(stmt->value)->value; + } + + // what to iterate + DValue *aggrval = toElemDtor(stmt->aggr); + + // get length and pointer + LLValue *niters = DtoArrayLen(aggrval); + LLValue *val = DtoArrayPtr(aggrval); + + if (niters->getType() != keytype) { + size_t sz1 = getTypeBitSize(niters->getType()); + size_t sz2 = getTypeBitSize(keytype); + if (sz1 < sz2) + niters = irs->ir->CreateZExt(niters, keytype, "foreachtrunckey"); + else if (sz1 > sz2) + niters = irs->ir->CreateTrunc(niters, keytype, "foreachtrunckey"); + else + niters = irs->ir->CreateBitCast(niters, keytype, "foreachtrunckey"); + } + + if (stmt->op == TOKforeach) { + new llvm::StoreInst(zerokey, keyvar, irs->scopebb()); + } else { + new llvm::StoreInst(niters, keyvar, irs->scopebb()); + } + + llvm::BasicBlock *condbb = + llvm::BasicBlock::Create(irs->context(), "foreachcond", irs->topfunc()); + llvm::BasicBlock *bodybb = + llvm::BasicBlock::Create(irs->context(), "foreachbody", irs->topfunc()); + llvm::BasicBlock *nextbb = + llvm::BasicBlock::Create(irs->context(), "foreachnext", irs->topfunc()); + llvm::BasicBlock *endbb = + llvm::BasicBlock::Create(irs->context(), "foreachend", irs->topfunc()); + + llvm::BranchInst::Create(condbb, irs->scopebb()); + + // condition + irs->scope() = IRScope(condbb); + + LLValue *done = 0; + LLValue *load = DtoLoad(keyvar); + if (stmt->op == TOKforeach) { + done = irs->ir->CreateICmpULT(load, niters); + } else if (stmt->op == TOKforeach_reverse) { + done = irs->ir->CreateICmpUGT(load, zerokey); + load = irs->ir->CreateSub(load, LLConstantInt::get(keytype, 1, false)); + DtoStore(load, keyvar); + } + llvm::BranchInst::Create(bodybb, endbb, done, irs->scopebb()); + + // init body + irs->scope() = IRScope(bodybb); + + // get value for this iteration + LLValue *loadedKey = irs->ir->CreateLoad(keyvar); + LLValue *gep = DtoGEP1(val, loadedKey); + + if (!stmt->value->isRef() && !stmt->value->isOut()) { + // Copy value to local variable, and use it as the value variable. + DVarValue dst(stmt->value->type, valvar); + DVarValue src(stmt->value->type, gep); + DtoAssign(stmt->loc, &dst, &src); + getIrLocal(stmt->value)->value = valvar; + } else { + // Use the GEP as the address of the value variable. + DtoRawVarDeclaration(stmt->value, gep); + } + + // emit body + irs->func()->scopes->pushLoopTarget(stmt, nextbb, endbb); + if (stmt->body) + stmt->body->accept(this); + irs->func()->scopes->popLoopTarget(); + + if (!irs->scopereturned()) + llvm::BranchInst::Create(nextbb, irs->scopebb()); + + // next + irs->scope() = IRScope(nextbb); + if (stmt->op == TOKforeach) { + LLValue *load = DtoLoad(keyvar); + load = irs->ir->CreateAdd(load, LLConstantInt::get(keytype, 1, false)); + DtoStore(load, keyvar); + } + llvm::BranchInst::Create(condbb, irs->scopebb()); + + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); + + // end + irs->scope() = IRScope(endbb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(ForeachRangeStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("ForeachRangeStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; + + // start a dwarf lexical block + irs->DBuilder.EmitBlockStart(stmt->loc); + + // evaluate lwr/upr + assert(stmt->lwr->type->isintegral()); + LLValue *lower = toElemDtor(stmt->lwr)->getRVal(); + assert(stmt->upr->type->isintegral()); + LLValue *upper = toElemDtor(stmt->upr)->getRVal(); + + // handle key + assert(stmt->key->type->isintegral()); + LLValue *keyval = DtoRawVarDeclaration(stmt->key); + + // store initial value in key + if (stmt->op == TOKforeach) + DtoStore(lower, keyval); + else + DtoStore(upper, keyval); + + // set up the block we'll need + llvm::BasicBlock *condbb = llvm::BasicBlock::Create( + irs->context(), "foreachrange_cond", irs->topfunc()); + llvm::BasicBlock *bodybb = llvm::BasicBlock::Create( + irs->context(), "foreachrange_body", irs->topfunc()); + llvm::BasicBlock *nextbb = llvm::BasicBlock::Create( + irs->context(), "foreachrange_next", irs->topfunc()); + llvm::BasicBlock *endbb = llvm::BasicBlock::Create( + irs->context(), "foreachrange_end", irs->topfunc()); + + // jump to condition + llvm::BranchInst::Create(condbb, irs->scopebb()); + + // CONDITION + irs->scope() = IRScope(condbb); + + // first we test that lwr < upr + lower = DtoLoad(keyval); + assert(lower->getType() == upper->getType()); + llvm::ICmpInst::Predicate cmpop; + if (isLLVMUnsigned(stmt->key->type)) { + cmpop = (stmt->op == TOKforeach) ? llvm::ICmpInst::ICMP_ULT + : llvm::ICmpInst::ICMP_UGT; + } else { + cmpop = (stmt->op == TOKforeach) ? llvm::ICmpInst::ICMP_SLT + : llvm::ICmpInst::ICMP_SGT; + } + LLValue *cond = irs->ir->CreateICmp(cmpop, lower, upper); + + // jump to the body if range is ok, to the end if not + llvm::BranchInst::Create(bodybb, endbb, cond, irs->scopebb()); + + // BODY + irs->scope() = IRScope(bodybb); + + // reverse foreach decrements here + if (stmt->op == TOKforeach_reverse) { + LLValue *v = DtoLoad(keyval); + LLValue *one = LLConstantInt::get(v->getType(), 1, false); + v = irs->ir->CreateSub(v, one); + DtoStore(v, keyval); + } + + // emit body + irs->func()->scopes->pushLoopTarget(stmt, nextbb, endbb); + if (stmt->body) + stmt->body->accept(this); + irs->func()->scopes->popLoopTarget(); + + // jump to next iteration + if (!irs->scopereturned()) + llvm::BranchInst::Create(nextbb, irs->scopebb()); + + // NEXT + irs->scope() = IRScope(nextbb); + + // forward foreach increments here + if (stmt->op == TOKforeach) { + LLValue *v = DtoLoad(keyval); + LLValue *one = LLConstantInt::get(v->getType(), 1, false); + v = irs->ir->CreateAdd(v, one); + DtoStore(v, keyval); + } + + // jump to condition + llvm::BranchInst::Create(condbb, irs->scopebb()); + + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); + + // END + irs->scope() = IRScope(endbb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(LabelStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("LabelStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + // if it's an inline asm label, we don't create a basicblock, just emit it + // in the asm + if (irs->asmBlock) { + IRAsmStmt *a = new IRAsmStmt; + std::stringstream label; + printLabelName(label, mangleExact(irs->func()->decl), + stmt->ident->toChars()); + label << ":"; + a->code = label.str(); + irs->asmBlock->s.push_back(a); + irs->asmBlock->internalLabels.push_back(stmt->ident); + + // disable inlining + irs->func()->setNeverInline(); + } else { + llvm::BasicBlock *labelBB = llvm::BasicBlock::Create( + irs->context(), llvm::Twine("label.") + stmt->ident->toChars(), + irs->topfunc()); + irs->func()->scopes->addLabelTarget(stmt->ident, labelBB); + + if (!irs->scopereturned()) + llvm::BranchInst::Create(labelBB, irs->scopebb()); + + irs->scope() = IRScope(labelBB); + } + + if (stmt->statement) { + stmt->statement->accept(this); + } + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(GotoStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("GotoStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + irs->DBuilder.EmitStopPoint(stmt->loc); + + emitCoverageLinecountInc(stmt->loc); + + DtoGoto(stmt->loc, stmt->label); + + // TODO: Should not be needed. + llvm::BasicBlock *bb = + llvm::BasicBlock::Create(irs->context(), "aftergoto", irs->topfunc()); + irs->scope() = IRScope(bb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(GotoDefaultStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("GotoDefaultStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; + + irs->DBuilder.EmitStopPoint(stmt->loc); + + emitCoverageLinecountInc(stmt->loc); + + assert(!irs->scopereturned()); + assert(stmt->sw->sdefault->bodyBB); #if 0 // TODO: Store switch scopes. DtoEnclosingHandlers(stmt->loc, stmt->sw); #endif - llvm::BranchInst::Create(stmt->sw->sdefault->bodyBB, irs->scopebb()); + llvm::BranchInst::Create(stmt->sw->sdefault->bodyBB, irs->scopebb()); - // TODO: Should not be needed. - llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "aftergotodefault", irs->topfunc()); - irs->scope() = IRScope(bb); + // TODO: Should not be needed. + llvm::BasicBlock *bb = llvm::BasicBlock::Create( + irs->context(), "aftergotodefault", irs->topfunc()); + irs->scope() = IRScope(bb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(GotoCaseStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("GotoCaseStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; + + irs->DBuilder.EmitStopPoint(stmt->loc); + + emitCoverageLinecountInc(stmt->loc); + + assert(!irs->scopereturned()); + if (!stmt->cs->bodyBB) { + stmt->cs->bodyBB = + llvm::BasicBlock::Create(irs->context(), "goto_case", irs->topfunc()); } - ////////////////////////////////////////////////////////////////////////// - - void visit(GotoCaseStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("GotoCaseStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; - - irs->DBuilder.EmitStopPoint(stmt->loc); - - emitCoverageLinecountInc(stmt->loc); - - assert(!irs->scopereturned()); - if (!stmt->cs->bodyBB) - { - stmt->cs->bodyBB = llvm::BasicBlock::Create(irs->context(), "goto_case", irs->topfunc()); - } - #if 0 // TODO: Store switch scopes. DtoEnclosingHandlers(stmt->loc, stmt->sw); #endif - llvm::BranchInst::Create(stmt->cs->bodyBB, irs->scopebb()); + llvm::BranchInst::Create(stmt->cs->bodyBB, irs->scopebb()); - // TODO: Should not be needed. - llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "aftergotocase", irs->topfunc()); - irs->scope() = IRScope(bb); + // TODO: Should not be needed. + llvm::BasicBlock *bb = llvm::BasicBlock::Create( + irs->context(), "aftergotocase", irs->topfunc()); + irs->scope() = IRScope(bb); + } + + ////////////////////////////////////////////////////////////////////////// + + void visit(WithStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("WithStatement::toIR(): %s", stmt->loc.toChars()); + LOG_SCOPE; + + irs->DBuilder.EmitBlockStart(stmt->loc); + + assert(stmt->exp); + + // with(..) can either be used with expressions or with symbols + // wthis == null indicates the symbol form + if (stmt->wthis) { + DValue *e = toElemDtor(stmt->exp); + LLValue *mem = DtoRawVarDeclaration(stmt->wthis); + DtoStore(e->getRVal(), mem); } - ////////////////////////////////////////////////////////////////////////// + if (stmt->body) + stmt->body->accept(this); - void visit(WithStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("WithStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + irs->DBuilder.EmitBlockEnd(); + } - irs->DBuilder.EmitBlockStart(stmt->loc); + ////////////////////////////////////////////////////////////////////////// - assert(stmt->exp); + void visit(SwitchErrorStatement *stmt) LLVM_OVERRIDE { + IF_LOG Logger::println("SwitchErrorStatement::toIR(): %s", + stmt->loc.toChars()); + LOG_SCOPE; - // with(..) can either be used with expressions or with symbols - // wthis == null indicates the symbol form - if (stmt->wthis) { - DValue* e = toElemDtor(stmt->exp); - LLValue* mem = DtoRawVarDeclaration(stmt->wthis); - DtoStore(e->getRVal(), mem); - } + llvm::Function *fn = + LLVM_D_GetRuntimeFunction(stmt->loc, irs->module, "_d_switch_error"); - if (stmt->body) - stmt->body->accept(this); + LLValue *moduleInfoSymbol = + getIrModule(irs->func()->decl->getModule())->moduleInfoSymbol(); + LLType *moduleInfoType = DtoType(Module::moduleinfo->type); - irs->DBuilder.EmitBlockEnd(); - } + LLCallSite call = irs->CreateCallOrInvoke( + fn, DtoBitCast(moduleInfoSymbol, getPtrToType(moduleInfoType)), + DtoConstUint(stmt->loc.linnum)); + call.setDoesNotReturn(); + } - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - void visit(SwitchErrorStatement *stmt) LLVM_OVERRIDE { - IF_LOG Logger::println("SwitchErrorStatement::toIR(): %s", stmt->loc.toChars()); - LOG_SCOPE; + void visit(AsmStatement *stmt) LLVM_OVERRIDE { AsmStatement_toIR(stmt, irs); } - llvm::Function* fn = LLVM_D_GetRuntimeFunction(stmt->loc, irs->module, "_d_switch_error"); + ////////////////////////////////////////////////////////////////////////// - LLValue *moduleInfoSymbol = getIrModule(irs->func()->decl->getModule())->moduleInfoSymbol(); - LLType *moduleInfoType = DtoType(Module::moduleinfo->type); + void visit(CompoundAsmStatement *stmt) LLVM_OVERRIDE { + CompoundAsmStatement_toIR(stmt, irs); + } - LLCallSite call = irs->CreateCallOrInvoke( - fn, - DtoBitCast(moduleInfoSymbol, getPtrToType(moduleInfoType)), - DtoConstUint(stmt->loc.linnum) - ); - call.setDoesNotReturn(); - } + ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// + void visit(ImportStatement *stmt) LLVM_OVERRIDE { + // Empty. + } - void visit(AsmStatement *stmt) LLVM_OVERRIDE { - AsmStatement_toIR(stmt, irs); - } + ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// + void visit(Statement *stmt) LLVM_OVERRIDE { + error(stmt->loc, "Statement type Statement not implemented: %s", + stmt->toChars()); + fatal(); + } - void visit(CompoundAsmStatement *stmt) LLVM_OVERRIDE{ - CompoundAsmStatement_toIR(stmt, irs); - } + ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - - void visit(ImportStatement *stmt) LLVM_OVERRIDE { - // Empty. - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(Statement *stmt) LLVM_OVERRIDE { - error(stmt->loc, "Statement type Statement not implemented: %s", stmt->toChars()); - fatal(); - } - - ////////////////////////////////////////////////////////////////////////// - - void visit(PragmaStatement *stmt) LLVM_OVERRIDE { - error(stmt->loc, "Statement type PragmaStatement not implemented: %s", stmt->toChars()); - fatal(); - } + void visit(PragmaStatement *stmt) LLVM_OVERRIDE { + error(stmt->loc, "Statement type PragmaStatement not implemented: %s", + stmt->toChars()); + fatal(); + } }; ////////////////////////////////////////////////////////////////////////////// -void Statement_toIR(Statement *s, IRState *irs) -{ - ToIRVisitor v(irs); - s->accept(&v); +void Statement_toIR(Statement *s, IRState *irs) { + ToIRVisitor v(irs); + s->accept(&v); } diff --git a/gen/structs.cpp b/gen/structs.cpp index 691f07ec88..2cda14ec12 100644 --- a/gen/structs.cpp +++ b/gen/structs.cpp @@ -28,68 +28,66 @@ ////////////////////////////////////////////////////////////////////////////////////////// -void DtoResolveStruct(StructDeclaration* sd) -{ - DtoResolveStruct(sd, sd->loc); -} +void DtoResolveStruct(StructDeclaration *sd) { DtoResolveStruct(sd, sd->loc); } -void DtoResolveStruct(StructDeclaration* sd, Loc& callerLoc) -{ - // Make sure to resolve each struct type exactly once. - if (sd->ir.isResolved()) return; - sd->ir.setResolved(); +void DtoResolveStruct(StructDeclaration *sd, Loc &callerLoc) { + // Make sure to resolve each struct type exactly once. + if (sd->ir.isResolved()) + return; + sd->ir.setResolved(); - IF_LOG Logger::println("Resolving struct type: %s (%s)", sd->toChars(), sd->loc.toChars()); - LOG_SCOPE; + IF_LOG Logger::println("Resolving struct type: %s (%s)", sd->toChars(), + sd->loc.toChars()); + LOG_SCOPE; - // make sure type exists - DtoType(sd->type); + // make sure type exists + DtoType(sd->type); - // if it's a forward declaration, all bets are off. The type should be enough - if (sd->sizeok != SIZEOKdone) - { - error(callerLoc, "struct %s.%s unknown size", sd->getModule()->toChars(), sd->toChars()); - fatal(); - } - - // create the IrAggr - getIrAggr(sd, true); - - // Set up our field metadata. - for (auto vd : sd->fields) - { - IF_LOG { - if (isIrFieldCreated(vd)) - Logger::println("struct field already exists"); - } - getIrField(vd, true); + // if it's a forward declaration, all bets are off. The type should be enough + if (sd->sizeok != SIZEOKdone) { + error(callerLoc, "struct %s.%s unknown size", sd->getModule()->toChars(), + sd->toChars()); + fatal(); + } + + // create the IrAggr + getIrAggr(sd, true); + + // Set up our field metadata. + for (auto vd : sd->fields) { + IF_LOG { + if (isIrFieldCreated(vd)) + Logger::println("struct field already exists"); } + getIrField(vd, true); + } } ////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////// D STRUCT UTILITIES //////////////////////////////////// +//////////////////////////// D STRUCT UTILITIES +/////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoStructEquals(TOK op, DValue* lhs, DValue* rhs) -{ - Type* t = lhs->getType()->toBasetype(); - assert(t->ty == Tstruct); +LLValue *DtoStructEquals(TOK op, DValue *lhs, DValue *rhs) { + Type *t = lhs->getType()->toBasetype(); + assert(t->ty == Tstruct); - // set predicate - llvm::ICmpInst::Predicate cmpop; - if (op == TOKequal || op == TOKidentity) - cmpop = llvm::ICmpInst::ICMP_EQ; - else - cmpop = llvm::ICmpInst::ICMP_NE; + // set predicate + llvm::ICmpInst::Predicate cmpop; + if (op == TOKequal || op == TOKidentity) + cmpop = llvm::ICmpInst::ICMP_EQ; + else + cmpop = llvm::ICmpInst::ICMP_NE; - // empty struct? EQ always true, NE always false - if (static_cast(t)->sym->fields.dim == 0) - return DtoConstBool(cmpop == llvm::ICmpInst::ICMP_EQ); + // empty struct? EQ always true, NE always false + if (static_cast(t)->sym->fields.dim == 0) + return DtoConstBool(cmpop == llvm::ICmpInst::ICMP_EQ); - // call memcmp - size_t sz = getTypePaddedSize(DtoType(t)); - LLValue* val = DtoMemCmp(lhs->getRVal(), rhs->getRVal(), DtoConstSize_t(sz)); - return gIR->ir->CreateICmp(cmpop, val, LLConstantInt::get(val->getType(), 0, false)); + // call memcmp + size_t sz = getTypePaddedSize(DtoType(t)); + LLValue *val = DtoMemCmp(lhs->getRVal(), rhs->getRVal(), DtoConstSize_t(sz)); + return gIR->ir->CreateICmp(cmpop, val, + LLConstantInt::get(val->getType(), 0, false)); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -97,75 +95,75 @@ LLValue* DtoStructEquals(TOK op, DValue* lhs, DValue* rhs) /// Return the type returned by DtoUnpaddedStruct called on a value of the /// specified type. /// Union types will get expanded into a struct, with a type for each member. -LLType* DtoUnpaddedStructType(Type* dty) { - assert(dty->ty == Tstruct); +LLType *DtoUnpaddedStructType(Type *dty) { + assert(dty->ty == Tstruct); - typedef llvm::DenseMap CacheT; - static llvm::ManagedStatic cache; - auto it = cache->find(dty); - if (it != cache->end()) - return it->second; + typedef llvm::DenseMap CacheT; + static llvm::ManagedStatic cache; + auto it = cache->find(dty); + if (it != cache->end()) + return it->second; - TypeStruct* sty = static_cast(dty); - VarDeclarations& fields = sty->sym->fields; + TypeStruct *sty = static_cast(dty); + VarDeclarations &fields = sty->sym->fields; - std::vector types; - types.reserve(fields.dim); + std::vector types; + types.reserve(fields.dim); - for (unsigned i = 0; i < fields.dim; i++) { - LLType* fty; - if (fields[i]->type->ty == Tstruct) { - // Nested structs are the only members that can contain padding - fty = DtoUnpaddedStructType(fields[i]->type); - } else { - fty = DtoType(fields[i]->type); - } - types.push_back(fty); + for (unsigned i = 0; i < fields.dim; i++) { + LLType *fty; + if (fields[i]->type->ty == Tstruct) { + // Nested structs are the only members that can contain padding + fty = DtoUnpaddedStructType(fields[i]->type); + } else { + fty = DtoType(fields[i]->type); } - LLStructType* Ty = LLStructType::get(gIR->context(), types); - cache->insert(std::make_pair(dty, Ty)); - return Ty; + types.push_back(fty); + } + LLStructType *Ty = LLStructType::get(gIR->context(), types); + cache->insert(std::make_pair(dty, Ty)); + return Ty; } /// Return the struct value represented by v without the padding fields. /// Unions will be expanded, with a value for each member. /// Note: v must be a pointer to a struct, but the return value will be a /// first-class struct value. -LLValue* DtoUnpaddedStruct(Type* dty, LLValue* v) { - assert(dty->ty == Tstruct); - TypeStruct* sty = static_cast(dty); - VarDeclarations& fields = sty->sym->fields; +LLValue *DtoUnpaddedStruct(Type *dty, LLValue *v) { + assert(dty->ty == Tstruct); + TypeStruct *sty = static_cast(dty); + VarDeclarations &fields = sty->sym->fields; - LLValue* newval = llvm::UndefValue::get(DtoUnpaddedStructType(dty)); + LLValue *newval = llvm::UndefValue::get(DtoUnpaddedStructType(dty)); - for (unsigned i = 0; i < fields.dim; i++) { - LLValue* fieldptr = DtoIndexAggregate(v, sty->sym, fields[i]); - LLValue* fieldval; - if (fields[i]->type->ty == Tstruct) { - // Nested structs are the only members that can contain padding - fieldval = DtoUnpaddedStruct(fields[i]->type, fieldptr); - } else { - fieldval = DtoLoad(fieldptr); - } - newval = DtoInsertValue(newval, fieldval, i); + for (unsigned i = 0; i < fields.dim; i++) { + LLValue *fieldptr = DtoIndexAggregate(v, sty->sym, fields[i]); + LLValue *fieldval; + if (fields[i]->type->ty == Tstruct) { + // Nested structs are the only members that can contain padding + fieldval = DtoUnpaddedStruct(fields[i]->type, fieldptr); + } else { + fieldval = DtoLoad(fieldptr); } - return newval; + newval = DtoInsertValue(newval, fieldval, i); + } + return newval; } /// Undo the transformation performed by DtoUnpaddedStruct, writing to lval. -void DtoPaddedStruct(Type* dty, LLValue* v, LLValue* lval) { - assert(dty->ty == Tstruct); - TypeStruct* sty = static_cast(dty); - VarDeclarations& fields = sty->sym->fields; +void DtoPaddedStruct(Type *dty, LLValue *v, LLValue *lval) { + assert(dty->ty == Tstruct); + TypeStruct *sty = static_cast(dty); + VarDeclarations &fields = sty->sym->fields; - for (unsigned i = 0; i < fields.dim; i++) { - LLValue* fieldptr = DtoIndexAggregate(lval, sty->sym, fields[i]); - LLValue* fieldval = DtoExtractValue(v, i); - if (fields[i]->type->ty == Tstruct) { - // Nested structs are the only members that can contain padding - DtoPaddedStruct(fields[i]->type, fieldval, fieldptr); - } else { - DtoStore(fieldval, fieldptr); - } + for (unsigned i = 0; i < fields.dim; i++) { + LLValue *fieldptr = DtoIndexAggregate(lval, sty->sym, fields[i]); + LLValue *fieldval = DtoExtractValue(v, i); + if (fields[i]->type->ty == Tstruct) { + // Nested structs are the only members that can contain padding + DtoPaddedStruct(fields[i]->type, fieldval, fieldptr); + } else { + DtoStore(fieldval, fieldptr); } + } } diff --git a/gen/structs.h b/gen/structs.h index 2c79138b17..79ac133611 100644 --- a/gen/structs.h +++ b/gen/structs.h @@ -22,11 +22,10 @@ class StructDeclaration; class StructInitializer; class Type; class VarDeclaration; -namespace llvm -{ - class Constant; - class Type; - class Value; +namespace llvm { +class Constant; +class Type; +class Value; } /** @@ -37,25 +36,24 @@ namespace llvm * callerLoc is the location of the expression which requires the struct type * (only for better diagnosis) */ -void DtoResolveStruct(StructDeclaration* sd); -void DtoResolveStruct(StructDeclaration* sd, Loc& callerLoc); - +void DtoResolveStruct(StructDeclaration *sd); +void DtoResolveStruct(StructDeclaration *sd, Loc &callerLoc); /// Returns a boolean=true if the two structs are equal. -llvm::Value* DtoStructEquals(TOK op, DValue* lhs, DValue* rhs); +llvm::Value *DtoStructEquals(TOK op, DValue *lhs, DValue *rhs); /// Return the type returned by DtoUnpaddedStruct called on a value of the /// specified type. /// Union types will get expanded into a struct, with a type for each member. -llvm::Type* DtoUnpaddedStructType(Type* dty); +llvm::Type *DtoUnpaddedStructType(Type *dty); /// Return the struct value represented by v without the padding fields. /// Unions will be expanded, with a value for each member. /// Note: v must be a pointer to a struct, but the return value will be a /// first-class struct value. -llvm::Value* DtoUnpaddedStruct(Type* dty, llvm::Value* v); +llvm::Value *DtoUnpaddedStruct(Type *dty, llvm::Value *v); /// Undo the transformation performed by DtoUnpaddedStruct, writing to lval. -void DtoPaddedStruct(Type* dty, llvm::Value* v, llvm::Value* lval); +void DtoPaddedStruct(Type *dty, llvm::Value *v, llvm::Value *lval); #endif diff --git a/gen/target.cpp b/gen/target.cpp index 2c6a58b721..6cb40b3996 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -29,60 +29,52 @@ int Target::c_longsize; int Target::c_long_doublesize; bool Target::reverseCppOverloads; -void Target::init() -{ - ptrsize = gDataLayout->getPointerSize(ADDRESS_SPACE); +void Target::init() { + ptrsize = gDataLayout->getPointerSize(ADDRESS_SPACE); - llvm::Type* real = DtoType(Type::basic[Tfloat80]); - realsize = gDataLayout->getTypeAllocSize(real); - realpad = realsize - gDataLayout->getTypeStoreSize(real); - realalignsize = gDataLayout->getABITypeAlignment(real); - c_longsize = global.params.is64bit ? 8 : 4; - c_long_doublesize = realsize; + llvm::Type *real = DtoType(Type::basic[Tfloat80]); + realsize = gDataLayout->getTypeAllocSize(real); + realpad = realsize - gDataLayout->getTypeStoreSize(real); + realalignsize = gDataLayout->getABITypeAlignment(real); + c_longsize = global.params.is64bit ? 8 : 4; + c_long_doublesize = realsize; - reverseCppOverloads = false; // DMC is not supported. + reverseCppOverloads = false; // DMC is not supported. } /****************************** * Return memory alignment size of type. */ -unsigned Target::alignsize (Type* type) -{ - assert (type->isTypeBasic()); - if (type->ty == Tvoid) return 1; - return gDataLayout->getABITypeAlignment(DtoType(type)); +unsigned Target::alignsize(Type *type) { + assert(type->isTypeBasic()); + if (type->ty == Tvoid) + return 1; + return gDataLayout->getABITypeAlignment(DtoType(type)); } /****************************** * Return field alignment size of type. */ -unsigned Target::fieldalign (Type* type) -{ - return DtoAlignment(type); -} +unsigned Target::fieldalign(Type *type) { return DtoAlignment(type); } // sizes based on those from tollvm.cpp:DtoMutexType() -unsigned Target::critsecsize() -{ +unsigned Target::critsecsize() { #if defined(_MSC_VER) - // Return sizeof(RTL_CRITICAL_SECTION) - return global.params.is64bit ? 40 : 24; + // Return sizeof(RTL_CRITICAL_SECTION) + return global.params.is64bit ? 40 : 24; #else - if (global.params.targetTriple.isOSWindows()) - return global.params.is64bit ? 40 : 24; - else if (global.params.targetTriple.getOS() == llvm::Triple::FreeBSD) - return sizeof(size_t); - else - return sizeof(pthread_mutex_t); + if (global.params.targetTriple.isOSWindows()) + return global.params.is64bit ? 40 : 24; + else if (global.params.targetTriple.getOS() == llvm::Triple::FreeBSD) + return sizeof(size_t); + else + return sizeof(pthread_mutex_t); #endif } -Type *Target::va_listType() -{ - return gABI->vaListType(); -} +Type *Target::va_listType() { return gABI->vaListType(); } /****************************** * Encode the given expression, which is assumed to be an rvalue literal @@ -90,63 +82,59 @@ Type *Target::va_listType() * This corresponds roughly to the idiom *(Type *)&e. */ -Expression *Target::paintAsType(Expression *e, Type *type) -{ - union - { - d_int32 int32value; - d_int64 int64value; - float float32value; - double float64value; - } u; +Expression *Target::paintAsType(Expression *e, Type *type) { + union { + d_int32 int32value; + d_int64 int64value; + float float32value; + double float64value; + } u; - assert(e->type->size() == type->size()); + assert(e->type->size() == type->size()); - switch (e->type->ty) - { - case Tint32: - case Tuns32: - u.int32value = (d_int32)e->toInteger(); - break; + switch (e->type->ty) { + case Tint32: + case Tuns32: + u.int32value = (d_int32)e->toInteger(); + break; - case Tint64: - case Tuns64: - u.int64value = (d_int64)e->toInteger(); - break; + case Tint64: + case Tuns64: + u.int64value = (d_int64)e->toInteger(); + break; - case Tfloat32: - u.float32value = e->toReal(); - break; + case Tfloat32: + u.float32value = e->toReal(); + break; - case Tfloat64: - u.float64value = e->toReal(); - break; + case Tfloat64: + u.float64value = e->toReal(); + break; - default: - assert(0); - } + default: + assert(0); + } - switch (type->ty) - { - case Tint32: - case Tuns32: - return new IntegerExp(e->loc, u.int32value, type); + switch (type->ty) { + case Tint32: + case Tuns32: + return new IntegerExp(e->loc, u.int32value, type); - case Tint64: - case Tuns64: - return new IntegerExp(e->loc, u.int64value, type); + case Tint64: + case Tuns64: + return new IntegerExp(e->loc, u.int64value, type); - case Tfloat32: - return new RealExp(e->loc, ldouble(u.float32value), type); + case Tfloat32: + return new RealExp(e->loc, ldouble(u.float32value), type); - case Tfloat64: - return new RealExp(e->loc, ldouble(u.float64value), type); + case Tfloat64: + return new RealExp(e->loc, ldouble(u.float64value), type); - default: - assert(0); - } + default: + assert(0); + } - return NULL; // avoid warning + return NULL; // avoid warning } /****************************** @@ -157,10 +145,9 @@ Expression *Target::paintAsType(Expression *e, Type *type) * 3: wrong base type */ -int Target::checkVectorType(int sz, Type *type) -{ - // FIXME: It is possible to query the LLVM target about supported vectors? - return 0; +int Target::checkVectorType(int sz, Type *type) { + // FIXME: It is possible to query the LLVM target about supported vectors? + return 0; } /****************************** @@ -169,7 +156,4 @@ int Target::checkVectorType(int sz, Type *type) * modules whose source are empty, but code gets injected * immediately after loading. */ -void Target::loadModule(Module *m) -{ -} - +void Target::loadModule(Module *m) {} diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 41400a4977..d026d13a8b 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -26,985 +26,961 @@ ////////////////////////////////////////////////////////////////////////////////////////// -IrFuncTy &DtoIrTypeFunction(DValue* fnval) -{ - if (DFuncValue* dfnval = fnval->isFunc()) - { - if (dfnval->func) - return getIrFunc(dfnval->func)->irFty; - } +IrFuncTy &DtoIrTypeFunction(DValue *fnval) { + if (DFuncValue *dfnval = fnval->isFunc()) { + if (dfnval->func) + return getIrFunc(dfnval->func)->irFty; + } - Type* type = stripModifiers(fnval->getType()->toBasetype()); - DtoType(type); - assert(type->ctype); - return type->ctype->getIrFuncTy(); + Type *type = stripModifiers(fnval->getType()->toBasetype()); + DtoType(type); + assert(type->ctype); + return type->ctype->getIrFuncTy(); } -TypeFunction* DtoTypeFunction(DValue* fnval) -{ - Type* type = fnval->getType()->toBasetype(); - if (type->ty == Tfunction) - { - return static_cast(type); - } - else if (type->ty == Tdelegate) - { - // FIXME: There is really no reason why the function type should be - // unmerged at this stage, but the frontend still seems to produce such - // cases; for example for the uint(uint) next type of the return type of - // (&zero)(), leading to a crash in DtoCallFunction: - // --- - // void test8198() { - // uint delegate(uint) zero() { return null; } - // auto a = (&zero)()(0); - // } - // --- - // Calling merge() here works around the symptoms, but does not fix the - // root cause. +TypeFunction *DtoTypeFunction(DValue *fnval) { + Type *type = fnval->getType()->toBasetype(); + if (type->ty == Tfunction) { + return static_cast(type); + } else if (type->ty == Tdelegate) { + // FIXME: There is really no reason why the function type should be + // unmerged at this stage, but the frontend still seems to produce such + // cases; for example for the uint(uint) next type of the return type of + // (&zero)(), leading to a crash in DtoCallFunction: + // --- + // void test8198() { + // uint delegate(uint) zero() { return null; } + // auto a = (&zero)()(0); + // } + // --- + // Calling merge() here works around the symptoms, but does not fix the + // root cause. - Type* next = type->nextOf()->merge(); - assert(next->ty == Tfunction); - return static_cast(next); - } + Type *next = type->nextOf()->merge(); + assert(next->ty == Tfunction); + return static_cast(next); + } - llvm_unreachable("Cannot get TypeFunction* from non lazy/function/delegate"); + llvm_unreachable("Cannot get TypeFunction* from non lazy/function/delegate"); } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoCallableValue(DValue* fn) -{ - Type* type = fn->getType()->toBasetype(); - if (type->ty == Tfunction) - { - return fn->getRVal(); - } - else if (type->ty == Tdelegate) - { - if (fn->isLVal()) - { - LLValue* dg = fn->getLVal(); - LLValue* funcptr = DtoGEPi(dg, 0, 1); - return DtoLoad(funcptr, ".funcptr"); - } - else - { - LLValue* dg = fn->getRVal(); - assert(isaStruct(dg)); - return gIR->ir->CreateExtractValue(dg, 1, ".funcptr"); - } +LLValue *DtoCallableValue(DValue *fn) { + Type *type = fn->getType()->toBasetype(); + if (type->ty == Tfunction) { + return fn->getRVal(); + } else if (type->ty == Tdelegate) { + if (fn->isLVal()) { + LLValue *dg = fn->getLVal(); + LLValue *funcptr = DtoGEPi(dg, 0, 1); + return DtoLoad(funcptr, ".funcptr"); + } else { + LLValue *dg = fn->getRVal(); + assert(isaStruct(dg)); + return gIR->ir->CreateExtractValue(dg, 1, ".funcptr"); } + } - llvm_unreachable("Not a callable type."); + llvm_unreachable("Not a callable type."); } ////////////////////////////////////////////////////////////////////////////////////////// -LLFunctionType* DtoExtractFunctionType(LLType* type) -{ - if (LLFunctionType* fty = isaFunction(type)) - return fty; - else if (LLPointerType* pty = isaPointer(type)) - { - if (LLFunctionType* fty = isaFunction(pty->getElementType())) - return fty; - } - return NULL; +LLFunctionType *DtoExtractFunctionType(LLType *type) { + if (LLFunctionType *fty = isaFunction(type)) + return fty; + else if (LLPointerType *pty = isaPointer(type)) { + if (LLFunctionType *fty = isaFunction(pty->getElementType())) + return fty; + } + return NULL; } ////////////////////////////////////////////////////////////////////////////////////////// -static void addExplicitArguments(std::vector& args, AttrSet& attrs, - IrFuncTy& irFty, LLFunctionType* callableTy, const std::vector& argvals, int numFormalParams) -{ - // Number of arguments added to the LLVM type that are implicit on the - // frontend side of things (this, context pointers, etc.) - const size_t implicitLLArgCount = args.size(); +static void addExplicitArguments(std::vector &args, AttrSet &attrs, + IrFuncTy &irFty, LLFunctionType *callableTy, + const std::vector &argvals, + int numFormalParams) { + // Number of arguments added to the LLVM type that are implicit on the + // frontend side of things (this, context pointers, etc.) + const size_t implicitLLArgCount = args.size(); - // Number of formal arguments in the LLVM type (i.e. excluding varargs). - const size_t formalLLArgCount = irFty.args.size(); + // Number of formal arguments in the LLVM type (i.e. excluding varargs). + const size_t formalLLArgCount = irFty.args.size(); - // The number of explicit arguments in the D call expression (including - // varargs), not all of which necessarily generate a LLVM argument. - const size_t explicitDArgCount = argvals.size(); + // The number of explicit arguments in the D call expression (including + // varargs), not all of which necessarily generate a LLVM argument. + const size_t explicitDArgCount = argvals.size(); - // construct and initialize an IrFuncTyArg object for each vararg - std::vector optionalIrArgs; - for (size_t i = numFormalParams; i < explicitDArgCount; i++) { - Type* argType = argvals[i]->getType(); - bool passByVal = gABI->passByVal(argType); + // construct and initialize an IrFuncTyArg object for each vararg + std::vector optionalIrArgs; + for (size_t i = numFormalParams; i < explicitDArgCount; i++) { + Type *argType = argvals[i]->getType(); + bool passByVal = gABI->passByVal(argType); - AttrBuilder initialAttrs; - if (passByVal) - initialAttrs.add(LLAttribute::ByVal); - else - initialAttrs.add(DtoShouldExtend(argType)); + AttrBuilder initialAttrs; + if (passByVal) + initialAttrs.add(LLAttribute::ByVal); + else + initialAttrs.add(DtoShouldExtend(argType)); - optionalIrArgs.push_back(new IrFuncTyArg(argType, passByVal, initialAttrs)); - optionalIrArgs.back()->parametersIdx = i; + optionalIrArgs.push_back(new IrFuncTyArg(argType, passByVal, initialAttrs)); + optionalIrArgs.back()->parametersIdx = i; + } + + // let the ABI rewrite the IrFuncTyArg objects + gABI->rewriteVarargs(irFty, optionalIrArgs); + + const size_t explicitLLArgCount = formalLLArgCount + optionalIrArgs.size(); + args.resize(implicitLLArgCount + explicitLLArgCount, + static_cast(0)); + + // Iterate the explicit arguments from left to right in the D source, + // which is the reverse of the LLVM order if irFty.reverseParams is true. + for (size_t i = 0; i < explicitLLArgCount; ++i) { + const bool isVararg = (i >= irFty.args.size()); + IrFuncTyArg *irArg = NULL; + if (isVararg) + irArg = optionalIrArgs[i - numFormalParams]; + else + irArg = irFty.args[i]; + + DValue *const argval = argvals[irArg->parametersIdx]; + Type *const argType = argval->getType(); + + llvm::Value *llVal = NULL; + if (isVararg) + llVal = irFty.putParam(*irArg, argval); + else + llVal = irFty.putParam(i, argval); + + const size_t llArgIdx = + implicitLLArgCount + + (irFty.reverseParams ? explicitLLArgCount - i - 1 : i); + llvm::Type *const callableArgType = + (isVararg ? NULL : callableTy->getParamType(llArgIdx)); + + // Hack around LDC assuming structs and static arrays are in memory: + // If the function wants a struct, and the argument value is a + // pointer to a struct, load from it before passing it in. + if (isaPointer(llVal) && DtoIsPassedByRef(argType) && + ((!isVararg && !isaPointer(callableArgType)) || + (isVararg && !irArg->byref && !irArg->isByVal()))) { + Logger::println("Loading struct type for function argument"); + llVal = DtoLoad(llVal); } - // let the ABI rewrite the IrFuncTyArg objects - gABI->rewriteVarargs(irFty, optionalIrArgs); - - const size_t explicitLLArgCount = formalLLArgCount + optionalIrArgs.size(); - args.resize(implicitLLArgCount + explicitLLArgCount, static_cast(0)); - - // Iterate the explicit arguments from left to right in the D source, - // which is the reverse of the LLVM order if irFty.reverseParams is true. - for (size_t i = 0; i < explicitLLArgCount; ++i) - { - const bool isVararg = (i >= irFty.args.size()); - IrFuncTyArg* irArg = NULL; - if (isVararg) - irArg = optionalIrArgs[i - numFormalParams]; - else - irArg = irFty.args[i]; - - DValue* const argval = argvals[irArg->parametersIdx]; - Type* const argType = argval->getType(); - - llvm::Value* llVal = NULL; - if (isVararg) - llVal = irFty.putParam(*irArg, argval); - else - llVal = irFty.putParam(i, argval); - - const size_t llArgIdx = implicitLLArgCount + - (irFty.reverseParams ? explicitLLArgCount - i - 1 : i); - llvm::Type* const callableArgType = - (isVararg ? NULL : callableTy->getParamType(llArgIdx)); - - // Hack around LDC assuming structs and static arrays are in memory: - // If the function wants a struct, and the argument value is a - // pointer to a struct, load from it before passing it in. - if (isaPointer(llVal) && DtoIsPassedByRef(argType) && - ((!isVararg && !isaPointer(callableArgType)) || - (isVararg && !irArg->byref && !irArg->isByVal()))) - { - Logger::println("Loading struct type for function argument"); - llVal = DtoLoad(llVal); - } - - // parameter type mismatch, this is hard to get rid of - if (!isVararg && llVal->getType() != callableArgType) - { - IF_LOG - { - Logger::cout() << "arg: " << *llVal << '\n'; - Logger::cout() << "expects: " << *callableArgType << '\n'; - } - if (isaStruct(llVal)) - llVal = DtoAggrPaint(llVal, callableArgType); - else - llVal = DtoBitCast(llVal, callableArgType); - } - - args[llArgIdx] = llVal; - // +1 as index 0 contains the function attributes. - attrs.add(llArgIdx + 1, irArg->attrs); - - if (isVararg) - delete irArg; + // parameter type mismatch, this is hard to get rid of + if (!isVararg && llVal->getType() != callableArgType) { + IF_LOG { + Logger::cout() << "arg: " << *llVal << '\n'; + Logger::cout() << "expects: " << *callableArgType << '\n'; + } + if (isaStruct(llVal)) + llVal = DtoAggrPaint(llVal, callableArgType); + else + llVal = DtoBitCast(llVal, callableArgType); } + + args[llArgIdx] = llVal; + // +1 as index 0 contains the function attributes. + attrs.add(llArgIdx + 1, irArg->attrs); + + if (isVararg) + delete irArg; + } } ////////////////////////////////////////////////////////////////////////////////////////// -static LLValue* getTypeinfoArrayArgumentForDVarArg(Expressions* arguments, int begin) -{ - IF_LOG Logger::println("doing d-style variadic arguments"); - LOG_SCOPE +static LLValue *getTypeinfoArrayArgumentForDVarArg(Expressions *arguments, + int begin) { + IF_LOG Logger::println("doing d-style variadic arguments"); + LOG_SCOPE - // number of non variadic args - IF_LOG Logger::println("num non vararg params = %d", begin); + // number of non variadic args + IF_LOG Logger::println("num non vararg params = %d", begin); - // get n args in arguments list - size_t n_arguments = arguments ? arguments->dim : 0; + // get n args in arguments list + size_t n_arguments = arguments ? arguments->dim : 0; - const size_t numVariadicArgs = n_arguments - begin; + const size_t numVariadicArgs = n_arguments - begin; - // build type info array - LLType* typeinfotype = DtoType(Type::dtypeinfo->type); - LLArrayType* typeinfoarraytype = LLArrayType::get(typeinfotype, numVariadicArgs); + // build type info array + LLType *typeinfotype = DtoType(Type::dtypeinfo->type); + LLArrayType *typeinfoarraytype = + LLArrayType::get(typeinfotype, numVariadicArgs); - llvm::GlobalVariable* typeinfomem = - new llvm::GlobalVariable(gIR->module, typeinfoarraytype, true, llvm::GlobalValue::InternalLinkage, NULL, "._arguments.storage"); - IF_LOG Logger::cout() << "_arguments storage: " << *typeinfomem << '\n'; + llvm::GlobalVariable *typeinfomem = new llvm::GlobalVariable( + gIR->module, typeinfoarraytype, true, llvm::GlobalValue::InternalLinkage, + NULL, "._arguments.storage"); + IF_LOG Logger::cout() << "_arguments storage: " << *typeinfomem << '\n'; - std::vector vtypeinfos; - vtypeinfos.reserve(n_arguments); - for (size_t i=begin; itype)); - } + std::vector vtypeinfos; + vtypeinfos.reserve(n_arguments); + for (size_t i = begin; i < n_arguments; i++) { + vtypeinfos.push_back(DtoTypeInfoOf((*arguments)[i]->type)); + } - // apply initializer - LLConstant* tiinits = LLConstantArray::get(typeinfoarraytype, vtypeinfos); - typeinfomem->setInitializer(tiinits); + // apply initializer + LLConstant *tiinits = LLConstantArray::get(typeinfoarraytype, vtypeinfos); + typeinfomem->setInitializer(tiinits); - // put data in d-array - LLConstant* pinits[] = { - DtoConstSize_t(numVariadicArgs), - llvm::ConstantExpr::getBitCast(typeinfomem, getPtrToType(typeinfotype)) - }; - LLType* tiarrty = DtoType(Type::dtypeinfo->type->arrayOf()); - tiinits = LLConstantStruct::get(isaStruct(tiarrty), llvm::ArrayRef(pinits)); - LLValue* typeinfoarrayparam = new llvm::GlobalVariable(gIR->module, tiarrty, - true, llvm::GlobalValue::InternalLinkage, tiinits, "._arguments.array"); + // put data in d-array + LLConstant *pinits[] = { + DtoConstSize_t(numVariadicArgs), + llvm::ConstantExpr::getBitCast(typeinfomem, getPtrToType(typeinfotype))}; + LLType *tiarrty = DtoType(Type::dtypeinfo->type->arrayOf()); + tiinits = LLConstantStruct::get(isaStruct(tiarrty), + llvm::ArrayRef(pinits)); + LLValue *typeinfoarrayparam = new llvm::GlobalVariable( + gIR->module, tiarrty, true, llvm::GlobalValue::InternalLinkage, tiinits, + "._arguments.array"); - return DtoLoad(typeinfoarrayparam); + return DtoLoad(typeinfoarrayparam); } ////////////////////////////////////////////////////////////////////////////////////////// -bool DtoLowerMagicIntrinsic(IRState* p, FuncDeclaration* fndecl, CallExp *e, DValue*& result) -{ - // va_start instruction - if (fndecl->llvmInternal == LLVMva_start) { - if (e->arguments->dim < 1 || e->arguments->dim > 2) { - e->error("va_start instruction expects 1 (or 2) arguments"); - fatal(); +bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, + DValue *&result) { + // va_start instruction + if (fndecl->llvmInternal == LLVMva_start) { + if (e->arguments->dim < 1 || e->arguments->dim > 2) { + e->error("va_start instruction expects 1 (or 2) arguments"); + fatal(); + } + LLValue *pAp = toElem((*e->arguments)[0])->getLVal(); // va_list* + // variadic extern(D) function with implicit _argptr? + if (LLValue *pArgptr = p->func()->_argptr) { + DtoStore(DtoLoad(pArgptr), pAp); // ap = _argptr + result = new DImValue(e->type, pAp); + } else { + LLValue *vaStartArg = gABI->prepareVaStart(pAp); + result = + new DImValue(e->type, p->ir->CreateCall(GET_INTRINSIC_DECL(vastart), + vaStartArg, "")); + } + return true; + } + + // va_copy instruction + if (fndecl->llvmInternal == LLVMva_copy) { + if (e->arguments->dim != 2) { + e->error("va_copy instruction expects 2 arguments"); + fatal(); + } + LLValue *pDest = toElem((*e->arguments)[0])->getLVal(); // va_list* + LLValue *src = toElem((*e->arguments)[1])->getRVal(); // va_list + gABI->vaCopy(pDest, src); + result = new DVarValue(e->type, pDest); + return true; + } + + // va_arg instruction + if (fndecl->llvmInternal == LLVMva_arg) { + if (e->arguments->dim != 1) { + e->error("va_arg instruction expects 1 argument"); + fatal(); + } + LLValue *pAp = toElem((*e->arguments)[0])->getLVal(); // va_list* + LLValue *vaArgArg = gABI->prepareVaArg(pAp); + LLType *llType = DtoType(e->type); + if (DtoIsPassedByRef(e->type)) + llType = getPtrToType(llType); + result = new DImValue(e->type, p->ir->CreateVAArg(vaArgArg, llType)); + return true; + } + + // C alloca + if (fndecl->llvmInternal == LLVMalloca) { + if (e->arguments->dim != 1) { + e->error("alloca expects 1 arguments"); + fatal(); + } + Expression *exp = (*e->arguments)[0]; + DValue *expv = toElem(exp); + if (expv->getType()->toBasetype()->ty != Tint32) + expv = DtoCast(e->loc, expv, Type::tint32); + result = new DImValue(e->type, + p->ir->CreateAlloca(LLType::getInt8Ty(p->context()), + expv->getRVal(), ".alloca")); + return true; + } + + // fence instruction + if (fndecl->llvmInternal == LLVMfence) { + if (e->arguments->dim != 1) { + e->error("fence instruction expects 1 arguments"); + fatal(); + } + p->ir->CreateFence(llvm::AtomicOrdering((*e->arguments)[0]->toInteger())); + return true; + } + + // atomic store instruction + if (fndecl->llvmInternal == LLVMatomic_store) { + if (e->arguments->dim != 3) { + e->error("atomic store instruction expects 3 arguments"); + fatal(); + } + Expression *exp1 = (*e->arguments)[0]; + Expression *exp2 = (*e->arguments)[1]; + int atomicOrdering = (*e->arguments)[2]->toInteger(); + LLValue *val = toElem(exp1)->getRVal(); + LLValue *ptr = toElem(exp2)->getRVal(); + + if (!val->getType()->isIntegerTy()) { + llvm::PointerType *v = isaPointer(val->getType()); + if (v && v->getContainedType(0)->isStructTy()) { + switch (const size_t N = getTypeBitSize(v->getContainedType(0))) { + case 8: + case 16: + case 32: + case 64: + case 128: + val = DtoLoad( + DtoBitCast(val, llvm::Type::getIntNPtrTy( + gIR->context(), static_cast(N)))); + ptr = DtoBitCast(ptr, llvm::Type::getIntNPtrTy( + gIR->context(), static_cast(N))); + break; + default: + goto errorStore; } - LLValue* pAp = toElem((*e->arguments)[0])->getLVal(); // va_list* - // variadic extern(D) function with implicit _argptr? - if (LLValue* pArgptr = p->func()->_argptr) { - DtoStore(DtoLoad(pArgptr), pAp); // ap = _argptr - result = new DImValue(e->type, pAp); - } else { - LLValue* vaStartArg = gABI->prepareVaStart(pAp); - result = new DImValue(e->type, p->ir->CreateCall( - GET_INTRINSIC_DECL(vastart), vaStartArg, "")); - } - return true; + } else { + errorStore: + e->error("atomic store only supports integer types, not '%s'", + exp1->type->toChars()); + fatal(); + } } - // va_copy instruction - if (fndecl->llvmInternal == LLVMva_copy) { - if (e->arguments->dim != 2) { - e->error("va_copy instruction expects 2 arguments"); - fatal(); - } - LLValue* pDest = toElem((*e->arguments)[0])->getLVal(); // va_list* - LLValue* src = toElem((*e->arguments)[1])->getRVal(); // va_list - gABI->vaCopy(pDest, src); - result = new DVarValue(e->type, pDest); - return true; + llvm::StoreInst *ret = p->ir->CreateStore(val, ptr); + ret->setAtomic(llvm::AtomicOrdering(atomicOrdering)); + ret->setAlignment(getTypeAllocSize(val->getType())); + return true; + } + + // atomic load instruction + if (fndecl->llvmInternal == LLVMatomic_load) { + if (e->arguments->dim != 2) { + e->error("atomic load instruction expects 2 arguments"); + fatal(); } - // va_arg instruction - if (fndecl->llvmInternal == LLVMva_arg) { - if (e->arguments->dim != 1) { - e->error("va_arg instruction expects 1 argument"); - fatal(); + Expression *exp = (*e->arguments)[0]; + int atomicOrdering = (*e->arguments)[1]->toInteger(); + + LLValue *ptr = toElem(exp)->getRVal(); + LLType *ptrTy = ptr->getType()->getContainedType(0); + Type *retType = exp->type->nextOf(); + + if (!ptrTy->isIntegerTy()) { + if (ptrTy->isStructTy()) { + switch (const size_t N = getTypeBitSize(ptrTy)) { + case 8: + case 16: + case 32: + case 64: + case 128: + ptr = DtoBitCast(ptr, llvm::Type::getIntNPtrTy( + gIR->context(), static_cast(N))); + break; + default: + goto errorLoad; } - LLValue* pAp = toElem((*e->arguments)[0])->getLVal(); // va_list* - LLValue* vaArgArg = gABI->prepareVaArg(pAp); - LLType* llType = DtoType(e->type); - if (DtoIsPassedByRef(e->type)) - llType = getPtrToType(llType); - result = new DImValue(e->type, p->ir->CreateVAArg(vaArgArg, llType)); - return true; + } else { + errorLoad: + e->error("atomic load only supports integer types, not '%s'", + retType->toChars()); + fatal(); + } } - // C alloca - if (fndecl->llvmInternal == LLVMalloca) { - if (e->arguments->dim != 1) { - e->error("alloca expects 1 arguments"); - fatal(); + llvm::LoadInst *load = p->ir->CreateLoad(ptr); + load->setAlignment(getTypeAllocSize(load->getType())); + load->setAtomic(llvm::AtomicOrdering(atomicOrdering)); + llvm::Value *val = load; + if (val->getType() != ptrTy) + val = DtoAllocaDump(val, retType); + result = new DImValue(retType, val); + return true; + } + + // cmpxchg instruction + if (fndecl->llvmInternal == LLVMatomic_cmp_xchg) { + if (e->arguments->dim != 4) { + e->error("cmpxchg instruction expects 4 arguments"); + fatal(); + } + Expression *exp1 = (*e->arguments)[0]; + Expression *exp2 = (*e->arguments)[1]; + Expression *exp3 = (*e->arguments)[2]; + int atomicOrdering = (*e->arguments)[3]->toInteger(); + LLValue *ptr = toElem(exp1)->getRVal(); + LLValue *cmp = toElem(exp2)->getRVal(); + LLValue *val = toElem(exp3)->getRVal(); + LLType *retTy = val->getType(); + + if (!cmp->getType()->isIntegerTy()) { + llvm::PointerType *v = isaPointer(cmp->getType()); + if (v && v->getContainedType(0)->isStructTy()) { + switch (const size_t N = getTypeBitSize(v->getContainedType(0))) { + case 8: + case 16: + case 32: + case 64: + case 128: + ptr = DtoBitCast(ptr, llvm::Type::getIntNPtrTy( + gIR->context(), static_cast(N))); + cmp = DtoLoad( + DtoBitCast(cmp, llvm::Type::getIntNPtrTy( + gIR->context(), static_cast(N)))); + val = DtoLoad( + DtoBitCast(val, llvm::Type::getIntNPtrTy( + gIR->context(), static_cast(N)))); + break; + default: + goto errorCmpxchg; } - Expression* exp = (*e->arguments)[0]; - DValue* expv = toElem(exp); - if (expv->getType()->toBasetype()->ty != Tint32) - expv = DtoCast(e->loc, expv, Type::tint32); - result = new DImValue(e->type, p->ir->CreateAlloca( - LLType::getInt8Ty(p->context()), expv->getRVal(), ".alloca")); - return true; + } else { + errorCmpxchg: + e->error("cmpxchg only supports integer types, not '%s'", + exp2->type->toChars()); + fatal(); + } + } + LLValue *ret = p->ir->CreateAtomicCmpXchg( + ptr, cmp, val, llvm::AtomicOrdering(atomicOrdering), + llvm::AtomicOrdering(atomicOrdering)); + // Use the same quickfix as for dragonegg - see r210956 + ret = p->ir->CreateExtractValue(ret, 0); + llvm::Value *retVal = ret; + if (retVal->getType() != retTy) + retVal = DtoAllocaDump(retVal, exp3->type); + result = new DImValue(exp3->type, retVal); + return true; + } + + // atomicrmw instruction + if (fndecl->llvmInternal == LLVMatomic_rmw) { + if (e->arguments->dim != 3) { + e->error("atomic_rmw instruction expects 3 arguments"); + fatal(); } - // fence instruction - if (fndecl->llvmInternal == LLVMfence) { - if (e->arguments->dim != 1) { - e->error("fence instruction expects 1 arguments"); - fatal(); - } - p->ir->CreateFence(llvm::AtomicOrdering((*e->arguments)[0]->toInteger())); - return true; + static const char *ops[] = {"xchg", "add", "sub", "and", "nand", "or", + "xor", "max", "min", "umax", "umin", 0}; + + int op = 0; + for (;; ++op) { + if (ops[op] == 0) { + e->error("unknown atomic_rmw operation %s", + fndecl->intrinsicName.c_str()); + fatal(); + } + if (fndecl->intrinsicName == ops[op]) + break; } - // atomic store instruction - if (fndecl->llvmInternal == LLVMatomic_store) { - if (e->arguments->dim != 3) { - e->error("atomic store instruction expects 3 arguments"); - fatal(); - } - Expression* exp1 = (*e->arguments)[0]; - Expression* exp2 = (*e->arguments)[1]; - int atomicOrdering = (*e->arguments)[2]->toInteger(); - LLValue* val = toElem(exp1)->getRVal(); - LLValue* ptr = toElem(exp2)->getRVal(); + Expression *exp1 = (*e->arguments)[0]; + Expression *exp2 = (*e->arguments)[1]; + int atomicOrdering = (*e->arguments)[2]->toInteger(); + LLValue *ptr = toElem(exp1)->getRVal(); + LLValue *val = toElem(exp2)->getRVal(); + LLValue *ret = + p->ir->CreateAtomicRMW(llvm::AtomicRMWInst::BinOp(op), ptr, val, + llvm::AtomicOrdering(atomicOrdering)); + result = new DImValue(exp2->type, ret); + return true; + } - if (!val->getType()->isIntegerTy()) { - llvm::PointerType *v = isaPointer(val->getType()); - if (v && v->getContainedType(0)->isStructTy()) { - switch (const size_t N = getTypeBitSize(v->getContainedType(0))) { - case 8: - case 16: - case 32: - case 64: - case 128: - val = DtoLoad(DtoBitCast(val, llvm::Type::getIntNPtrTy(gIR->context(), static_cast(N)))); - ptr = DtoBitCast(ptr, llvm::Type::getIntNPtrTy(gIR->context(), static_cast(N))); - break; - default: - goto errorStore; - } - } - else { -errorStore: - e->error("atomic store only supports integer types, not '%s'", exp1->type->toChars()); - fatal(); - } - } - - llvm::StoreInst* ret = p->ir->CreateStore(val, ptr); - ret->setAtomic(llvm::AtomicOrdering(atomicOrdering)); - ret->setAlignment(getTypeAllocSize(val->getType())); - return true; + // bitop + if (fndecl->llvmInternal == LLVMbitop_bt || + fndecl->llvmInternal == LLVMbitop_btr || + fndecl->llvmInternal == LLVMbitop_btc || + fndecl->llvmInternal == LLVMbitop_bts) { + if (e->arguments->dim != 2) { + e->error("bitop intrinsic expects 2 arguments"); + fatal(); } - // atomic load instruction - if (fndecl->llvmInternal == LLVMatomic_load) { - if (e->arguments->dim != 2) { - e->error("atomic load instruction expects 2 arguments"); - fatal(); - } + Expression *exp1 = (*e->arguments)[0]; + Expression *exp2 = (*e->arguments)[1]; + LLValue *ptr = toElem(exp1)->getRVal(); + LLValue *bitnum = toElem(exp2)->getRVal(); - Expression* exp = (*e->arguments)[0]; - int atomicOrdering = (*e->arguments)[1]->toInteger(); + unsigned bitmask = DtoSize_t()->getBitWidth() - 1; + assert(bitmask == 31 || bitmask == 63); + // auto q = cast(size_t*)ptr + (bitnum >> (64bit ? 6 : 5)); + LLValue *q = DtoBitCast(ptr, DtoSize_t()->getPointerTo()); + q = DtoGEP1(q, p->ir->CreateLShr(bitnum, bitmask == 63 ? 6 : 5), "bitop.q"); - LLValue* ptr = toElem(exp)->getRVal(); - LLType* ptrTy = ptr->getType()->getContainedType(0); - Type* retType = exp->type->nextOf(); + // auto mask = 1 << (bitnum & bitmask); + LLValue *mask = + p->ir->CreateAnd(bitnum, DtoConstSize_t(bitmask), "bitop.tmp"); + mask = p->ir->CreateShl(DtoConstSize_t(1), mask, "bitop.mask"); - if (!ptrTy->isIntegerTy()) { - if (ptrTy->isStructTy()) { - switch (const size_t N = getTypeBitSize(ptrTy)) { - case 8: - case 16: - case 32: - case 64: - case 128: - ptr = DtoBitCast(ptr, llvm::Type::getIntNPtrTy(gIR->context(), static_cast(N))); - break; - default: - goto errorLoad; - } - } - else { -errorLoad: - e->error("atomic load only supports integer types, not '%s'", retType->toChars()); - fatal(); - } - } + // auto result = (*q & mask) ? -1 : 0; + LLValue *val = + p->ir->CreateZExt(DtoLoad(q, "bitop.tmp"), DtoSize_t(), "bitop.val"); + LLValue *ret = p->ir->CreateAnd(val, mask, "bitop.tmp"); + ret = p->ir->CreateICmpNE(ret, DtoConstSize_t(0), "bitop.tmp"); + ret = p->ir->CreateSelect(ret, DtoConstInt(-1), DtoConstInt(0), + "bitop.result"); - llvm::LoadInst* load = p->ir->CreateLoad(ptr); - load->setAlignment(getTypeAllocSize(load->getType())); - load->setAtomic(llvm::AtomicOrdering(atomicOrdering)); - llvm::Value* val = load; - if (val->getType() != ptrTy) - val = DtoAllocaDump(val, retType); - result = new DImValue(retType, val); - return true; + if (fndecl->llvmInternal != LLVMbitop_bt) { + llvm::Instruction::BinaryOps op; + if (fndecl->llvmInternal == LLVMbitop_btc) { + // *q ^= mask; + op = llvm::Instruction::Xor; + } else if (fndecl->llvmInternal == LLVMbitop_btr) { + // *q &= ~mask; + mask = p->ir->CreateNot(mask); + op = llvm::Instruction::And; + } else if (fndecl->llvmInternal == LLVMbitop_bts) { + // *q |= mask; + op = llvm::Instruction::Or; + } else { + llvm_unreachable("Unrecognized bitop intrinsic."); + } + + LLValue *newVal = p->ir->CreateBinOp(op, val, mask, "bitop.new_val"); + newVal = p->ir->CreateTrunc(newVal, DtoSize_t(), "bitop.tmp"); + DtoStore(newVal, q); } - // cmpxchg instruction - if (fndecl->llvmInternal == LLVMatomic_cmp_xchg) { - if (e->arguments->dim != 4) { - e->error("cmpxchg instruction expects 4 arguments"); - fatal(); - } - Expression* exp1 = (*e->arguments)[0]; - Expression* exp2 = (*e->arguments)[1]; - Expression* exp3 = (*e->arguments)[2]; - int atomicOrdering = (*e->arguments)[3]->toInteger(); - LLValue* ptr = toElem(exp1)->getRVal(); - LLValue* cmp = toElem(exp2)->getRVal(); - LLValue* val = toElem(exp3)->getRVal(); - LLType* retTy = val->getType(); + result = new DImValue(e->type, ret); + return true; + } - if (!cmp->getType()->isIntegerTy()) { - llvm::PointerType *v = isaPointer(cmp->getType()); - if (v && v->getContainedType(0)->isStructTy()) { - switch (const size_t N = getTypeBitSize(v->getContainedType(0))) { - case 8: - case 16: - case 32: - case 64: - case 128: - ptr = DtoBitCast(ptr, llvm::Type::getIntNPtrTy(gIR->context(), static_cast(N))); - cmp = DtoLoad(DtoBitCast(cmp, llvm::Type::getIntNPtrTy(gIR->context(), static_cast(N)))); - val = DtoLoad(DtoBitCast(val, llvm::Type::getIntNPtrTy(gIR->context(), static_cast(N)))); - break; - default: - goto errorCmpxchg; - } - } - else { -errorCmpxchg: - e->error("cmpxchg only supports integer types, not '%s'", exp2->type->toChars()); - fatal(); - } - } - LLValue* ret = p->ir->CreateAtomicCmpXchg(ptr, cmp, val, llvm::AtomicOrdering(atomicOrdering), llvm::AtomicOrdering(atomicOrdering)); - // Use the same quickfix as for dragonegg - see r210956 - ret = p->ir->CreateExtractValue(ret, 0); - llvm::Value* retVal = ret; - if (retVal->getType() != retTy) - retVal = DtoAllocaDump(retVal, exp3->type); - result = new DImValue(exp3->type, retVal); - return true; + if (fndecl->llvmInternal == LLVMbitop_vld) { + if (e->arguments->dim != 1) { + e->error("bitop.vld intrinsic expects 1 argument"); + fatal(); } + // TODO: Check types - // atomicrmw instruction - if (fndecl->llvmInternal == LLVMatomic_rmw) { - if (e->arguments->dim != 3) { - e->error("atomic_rmw instruction expects 3 arguments"); - fatal(); - } + Expression *exp1 = (*e->arguments)[0]; + LLValue *ptr = toElem(exp1)->getRVal(); + result = new DImValue(exp1->type, DtoVolatileLoad(ptr)); + return true; + } - static const char *ops[] = { - "xchg", - "add", - "sub", - "and", - "nand", - "or", - "xor", - "max", - "min", - "umax", - "umin", - 0 - }; - - int op = 0; - for (; ; ++op) { - if (ops[op] == 0) { - e->error("unknown atomic_rmw operation %s", fndecl->intrinsicName.c_str()); - fatal(); - } - if (fndecl->intrinsicName == ops[op]) - break; - } - - Expression* exp1 = (*e->arguments)[0]; - Expression* exp2 = (*e->arguments)[1]; - int atomicOrdering = (*e->arguments)[2]->toInteger(); - LLValue* ptr = toElem(exp1)->getRVal(); - LLValue* val = toElem(exp2)->getRVal(); - LLValue* ret = p->ir->CreateAtomicRMW(llvm::AtomicRMWInst::BinOp(op), ptr, val, - llvm::AtomicOrdering(atomicOrdering)); - result = new DImValue(exp2->type, ret); - return true; + if (fndecl->llvmInternal == LLVMbitop_vst) { + if (e->arguments->dim != 2) { + e->error("bitop.vst intrinsic expects 2 arguments"); + fatal(); } + // TODO: Check types - // bitop - if (fndecl->llvmInternal == LLVMbitop_bt || - fndecl->llvmInternal == LLVMbitop_btr|| - fndecl->llvmInternal == LLVMbitop_btc|| - fndecl->llvmInternal == LLVMbitop_bts) - { - if (e->arguments->dim != 2) { - e->error("bitop intrinsic expects 2 arguments"); - fatal(); - } + Expression *exp1 = (*e->arguments)[0]; + Expression *exp2 = (*e->arguments)[1]; + LLValue *ptr = toElem(exp1)->getRVal(); + LLValue *val = toElem(exp2)->getRVal(); + DtoVolatileStore(val, ptr); + return true; + } - Expression* exp1 = (*e->arguments)[0]; - Expression* exp2 = (*e->arguments)[1]; - LLValue* ptr = toElem(exp1)->getRVal(); - LLValue* bitnum = toElem(exp2)->getRVal(); - - unsigned bitmask = DtoSize_t()->getBitWidth() - 1; - assert(bitmask == 31 || bitmask == 63); - // auto q = cast(size_t*)ptr + (bitnum >> (64bit ? 6 : 5)); - LLValue* q = DtoBitCast(ptr, DtoSize_t()->getPointerTo()); - q = DtoGEP1(q, p->ir->CreateLShr(bitnum, bitmask == 63 ? 6 : 5), "bitop.q"); - - // auto mask = 1 << (bitnum & bitmask); - LLValue* mask = p->ir->CreateAnd(bitnum, DtoConstSize_t(bitmask), "bitop.tmp"); - mask = p->ir->CreateShl(DtoConstSize_t(1), mask, "bitop.mask"); - - // auto result = (*q & mask) ? -1 : 0; - LLValue* val = p->ir->CreateZExt(DtoLoad(q, "bitop.tmp"), DtoSize_t(), "bitop.val"); - LLValue* ret = p->ir->CreateAnd(val, mask, "bitop.tmp"); - ret = p->ir->CreateICmpNE(ret, DtoConstSize_t(0), "bitop.tmp"); - ret = p->ir->CreateSelect(ret, DtoConstInt(-1), DtoConstInt(0), "bitop.result"); - - if (fndecl->llvmInternal != LLVMbitop_bt) { - llvm::Instruction::BinaryOps op; - if (fndecl->llvmInternal == LLVMbitop_btc) { - // *q ^= mask; - op = llvm::Instruction::Xor; - } else if (fndecl->llvmInternal == LLVMbitop_btr) { - // *q &= ~mask; - mask = p->ir->CreateNot(mask); - op = llvm::Instruction::And; - } else if (fndecl->llvmInternal == LLVMbitop_bts) { - // *q |= mask; - op = llvm::Instruction::Or; - } else { - llvm_unreachable("Unrecognized bitop intrinsic."); - } - - LLValue *newVal = p->ir->CreateBinOp(op, val, mask, "bitop.new_val"); - newVal = p->ir->CreateTrunc(newVal, DtoSize_t(), "bitop.tmp"); - DtoStore(newVal, q); - } - - result = new DImValue(e->type, ret); - return true; - } - - if (fndecl->llvmInternal == LLVMbitop_vld) - { - if (e->arguments->dim != 1) { - e->error("bitop.vld intrinsic expects 1 argument"); - fatal(); - } - // TODO: Check types - - Expression* exp1 = (*e->arguments)[0]; - LLValue* ptr = toElem(exp1)->getRVal(); - result = new DImValue(exp1->type, DtoVolatileLoad(ptr)); - return true; - } - - if (fndecl->llvmInternal == LLVMbitop_vst) - { - if (e->arguments->dim != 2) { - e->error("bitop.vst intrinsic expects 2 arguments"); - fatal(); - } - // TODO: Check types - - Expression* exp1 = (*e->arguments)[0]; - Expression* exp2 = (*e->arguments)[1]; - LLValue* ptr = toElem(exp1)->getRVal(); - LLValue* val = toElem(exp2)->getRVal(); - DtoVolatileStore(val, ptr); - return true; - } - - return false; + return false; } ////////////////////////////////////////////////////////////////////////////////////////// -class ImplicitArgumentsBuilder -{ +class ImplicitArgumentsBuilder { public: - ImplicitArgumentsBuilder(std::vector& args, AttrSet& attrs, Loc& loc, DValue* fnval, - LLFunctionType* llCalleeType, Expressions* arguments, Type* resulttype, LLValue* retvar) - : args(args), attrs(attrs), loc(loc), fnval(fnval) - , llCalleeType(llCalleeType), arguments(arguments), resulttype(resulttype), retvar(retvar) + ImplicitArgumentsBuilder(std::vector &args, AttrSet &attrs, + Loc &loc, DValue *fnval, + LLFunctionType *llCalleeType, Expressions *arguments, + Type *resulttype, LLValue *retvar) + : args(args), attrs(attrs), loc(loc), fnval(fnval), + llCalleeType(llCalleeType), arguments(arguments), + resulttype(resulttype), retvar(retvar) // computed: - , calleeType(fnval->getType()), dfnval(fnval->isFunc()) - , irFty(DtoIrTypeFunction(fnval)), tf(DtoTypeFunction(fnval)) - , llArgTypesBegin(llCalleeType->param_begin()) - {} + , + calleeType(fnval->getType()), dfnval(fnval->isFunc()), + irFty(DtoIrTypeFunction(fnval)), tf(DtoTypeFunction(fnval)), + llArgTypesBegin(llCalleeType->param_begin()) {} - void addImplicitArgs() - { - if (gABI->passThisBeforeSret(tf)) - { - addContext(); - addSret(); - } - else - { - addSret(); - addContext(); - } - - addArguments(); + void addImplicitArgs() { + if (gABI->passThisBeforeSret(tf)) { + addContext(); + addSret(); + } else { + addSret(); + addContext(); } + addArguments(); + } + private: - // passed: - std::vector& args; - AttrSet& attrs; - Loc& loc; - DValue* const fnval; - LLFunctionType* const llCalleeType; - Expressions* const arguments; - Type* const resulttype; - LLValue* const retvar; + // passed: + std::vector &args; + AttrSet &attrs; + Loc &loc; + DValue *const fnval; + LLFunctionType *const llCalleeType; + Expressions *const arguments; + Type *const resulttype; + LLValue *const retvar; - // computed: - Type* const calleeType; - DFuncValue* const dfnval; - IrFuncTy& irFty; - TypeFunction* const tf; - LLFunctionType::param_iterator llArgTypesBegin; + // computed: + Type *const calleeType; + DFuncValue *const dfnval; + IrFuncTy &irFty; + TypeFunction *const tf; + LLFunctionType::param_iterator llArgTypesBegin; - // Adds an optional sret pointer argument. - void addSret() - { - if (!irFty.arg_sret) - return; + // Adds an optional sret pointer argument. + void addSret() { + if (!irFty.arg_sret) + return; - size_t index = args.size(); - LLType* llArgType = *(llArgTypesBegin + index); + size_t index = args.size(); + LLType *llArgType = *(llArgTypesBegin + index); - LLValue* var = retvar; - if (!var) - var = DtoRawAlloca(llArgType->getContainedType(0), DtoAlignment(resulttype), ".rettmp"); + LLValue *var = retvar; + if (!var) + var = DtoRawAlloca(llArgType->getContainedType(0), + DtoAlignment(resulttype), ".rettmp"); - args.push_back(var); - attrs.add(index + 1, irFty.arg_sret->attrs); + args.push_back(var); + attrs.add(index + 1, irFty.arg_sret->attrs); - // verify that sret and/or inreg attributes are set - const AttrBuilder& sretAttrs = irFty.arg_sret->attrs; - assert((sretAttrs.contains(LLAttribute::StructRet) || sretAttrs.contains(LLAttribute::InReg)) - && "Sret arg not sret or inreg?"); + // verify that sret and/or inreg attributes are set + const AttrBuilder &sretAttrs = irFty.arg_sret->attrs; + assert((sretAttrs.contains(LLAttribute::StructRet) || + sretAttrs.contains(LLAttribute::InReg)) && + "Sret arg not sret or inreg?"); + } + + // Adds an optional context/this pointer argument. + void addContext() { + bool thiscall = irFty.arg_this; + bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate); + bool nestedcall = irFty.arg_nest; + + if (!thiscall && !delegatecall && !nestedcall) + return; + + size_t index = args.size(); + LLType *llArgType = *(llArgTypesBegin + index); + + if (dfnval && (dfnval->func->ident == Id::ensure || + dfnval->func->ident == Id::require)) { + // can be the this "context" argument for a contract invocation + // (in D2, we do not generate a full nested contexts for + // __require/__ensure as the needed parameters are passed + // explicitly, while in D1, the normal nested function handling + // mechanisms are used) + LLValue *thisarg = + DtoBitCast(DtoLoad(gIR->func()->thisArg), getVoidPtrType()); + args.push_back(thisarg); + } else if (thiscall && dfnval && dfnval->vthis) { + // ... or a normal 'this' argument + LLValue *thisarg = DtoBitCast(dfnval->vthis, llArgType); + args.push_back(thisarg); + } else if (delegatecall) { + // ... or a delegate context arg + LLValue *ctxarg; + if (fnval->isLVal()) + ctxarg = DtoLoad(DtoGEPi(fnval->getLVal(), 0, 0), ".ptr"); + else + ctxarg = gIR->ir->CreateExtractValue(fnval->getRVal(), 0, ".ptr"); + ctxarg = DtoBitCast(ctxarg, llArgType); + args.push_back(ctxarg); + } else if (nestedcall) { + // ... or a nested function context arg + if (dfnval) { + LLValue *contextptr = DtoNestedContext(loc, dfnval->func); + contextptr = DtoBitCast(contextptr, getVoidPtrType()); + args.push_back(contextptr); + } else + args.push_back(llvm::UndefValue::get(getVoidPtrType())); + } else { + error(loc, "Context argument required but none given"); + fatal(); } - // Adds an optional context/this pointer argument. - void addContext() - { - bool thiscall = irFty.arg_this; - bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate); - bool nestedcall = irFty.arg_nest; + // add attributes + if (irFty.arg_this) + attrs.add(index + 1, irFty.arg_this->attrs); + else if (irFty.arg_nest) + attrs.add(index + 1, irFty.arg_nest->attrs); + } - if (!thiscall && !delegatecall && !nestedcall) - return; + // D vararg functions need a "TypeInfo[] _arguments" argument. + void addArguments() { + if (!irFty.arg_arguments) + return; - size_t index = args.size(); - LLType* llArgType = *(llArgTypesBegin + index); + int numFormalParams = Parameter::dim(tf->parameters); + LLValue *argumentsArg = + getTypeinfoArrayArgumentForDVarArg(arguments, numFormalParams); - if (dfnval && (dfnval->func->ident == Id::ensure || dfnval->func->ident == Id::require)) - { - // can be the this "context" argument for a contract invocation - // (in D2, we do not generate a full nested contexts for - // __require/__ensure as the needed parameters are passed - // explicitly, while in D1, the normal nested function handling - // mechanisms are used) - LLValue* thisarg = DtoBitCast(DtoLoad(gIR->func()->thisArg), getVoidPtrType()); - args.push_back(thisarg); - } - else if (thiscall && dfnval && dfnval->vthis) - { - // ... or a normal 'this' argument - LLValue* thisarg = DtoBitCast(dfnval->vthis, llArgType); - args.push_back(thisarg); - } - else if (delegatecall) - { - // ... or a delegate context arg - LLValue* ctxarg; - if (fnval->isLVal()) - ctxarg = DtoLoad(DtoGEPi(fnval->getLVal(), 0, 0), ".ptr"); - else - ctxarg = gIR->ir->CreateExtractValue(fnval->getRVal(), 0, ".ptr"); - ctxarg = DtoBitCast(ctxarg, llArgType); - args.push_back(ctxarg); - } - else if (nestedcall) - { - // ... or a nested function context arg - if (dfnval) - { - LLValue* contextptr = DtoNestedContext(loc, dfnval->func); - contextptr = DtoBitCast(contextptr, getVoidPtrType()); - args.push_back(contextptr); - } - else - args.push_back(llvm::UndefValue::get(getVoidPtrType())); - } - else - { - error(loc, "Context argument required but none given"); - fatal(); - } - - // add attributes - if (irFty.arg_this) - attrs.add(index + 1, irFty.arg_this->attrs); - else if (irFty.arg_nest) - attrs.add(index + 1, irFty.arg_nest->attrs); - } - - // D vararg functions need a "TypeInfo[] _arguments" argument. - void addArguments() - { - if (!irFty.arg_arguments) - return; - - int numFormalParams = Parameter::dim(tf->parameters); - LLValue* argumentsArg = getTypeinfoArrayArgumentForDVarArg(arguments, numFormalParams); - - args.push_back(argumentsArg); - attrs.add(args.size(), irFty.arg_arguments->attrs); - } + args.push_back(argumentsArg); + attrs.add(args.size(), irFty.arg_arguments->attrs); + } }; ////////////////////////////////////////////////////////////////////////////////////////// // FIXME: this function is a mess ! -DValue* DtoCallFunction(Loc& loc, Type* resulttype, DValue* fnval, Expressions* arguments, llvm::Value* retvar) -{ - IF_LOG Logger::println("DtoCallFunction()"); - LOG_SCOPE +DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, + Expressions *arguments, llvm::Value *retvar) { + IF_LOG Logger::println("DtoCallFunction()"); + LOG_SCOPE - // make sure the D callee type has been processed - DtoType(fnval->getType()); + // make sure the D callee type has been processed + DtoType(fnval->getType()); - // get func value if any - DFuncValue* dfnval = fnval->isFunc(); + // get func value if any + DFuncValue *dfnval = fnval->isFunc(); - // get function type info - IrFuncTy& irFty = DtoIrTypeFunction(fnval); - TypeFunction* const tf = DtoTypeFunction(fnval); - Type* const returntype = tf->next; - const TY returnTy = returntype->toBasetype()->ty; + // get function type info + IrFuncTy &irFty = DtoIrTypeFunction(fnval); + TypeFunction *const tf = DtoTypeFunction(fnval); + Type *const returntype = tf->next; + const TY returnTy = returntype->toBasetype()->ty; - if (resulttype == NULL) - resulttype = returntype; + if (resulttype == NULL) + resulttype = returntype; - // get callee llvm value - LLValue* const callable = DtoCallableValue(fnval); - LLFunctionType* const callableTy = DtoExtractFunctionType(callable->getType()); - assert(callableTy); - const llvm::CallingConv::ID callconv = gABI->callingConv(callableTy, tf->linkage); + // get callee llvm value + LLValue *const callable = DtoCallableValue(fnval); + LLFunctionType *const callableTy = + DtoExtractFunctionType(callable->getType()); + assert(callableTy); + const llvm::CallingConv::ID callconv = + gABI->callingConv(callableTy, tf->linkage); -// IF_LOG Logger::cout() << "callable: " << *callable << '\n'; + // IF_LOG Logger::cout() << "callable: " << *callable << '\n'; - // parameter attributes - AttrSet attrs; + // parameter attributes + AttrSet attrs; - // return attrs - attrs.add(0, irFty.ret->attrs); + // return attrs + attrs.add(0, irFty.ret->attrs); - std::vector args; - args.reserve(irFty.args.size()); + std::vector args; + args.reserve(irFty.args.size()); - // handle implicit arguments (sret, context/this, _arguments) - ImplicitArgumentsBuilder iab(args, attrs, loc, fnval, callableTy, arguments, resulttype, retvar); - iab.addImplicitArgs(); + // handle implicit arguments (sret, context/this, _arguments) + ImplicitArgumentsBuilder iab(args, attrs, loc, fnval, callableTy, arguments, + resulttype, retvar); + iab.addImplicitArgs(); - // handle explicit arguments + // handle explicit arguments - Logger::println("doing normal arguments"); - IF_LOG { - Logger::println("Arguments so far: (%d)", static_cast(args.size())); - Logger::indent(); - for (size_t i = 0; i < args.size(); i++) { - Logger::cout() << *args[i] << '\n'; - } - Logger::undent(); - Logger::cout() << "Function type: " << tf->toChars() << '\n'; - //Logger::cout() << "LLVM functype: " << *callable->getType() << '\n'; + Logger::println("doing normal arguments"); + IF_LOG { + Logger::println("Arguments so far: (%d)", static_cast(args.size())); + Logger::indent(); + for (size_t i = 0; i < args.size(); i++) { + Logger::cout() << *args[i] << '\n'; } + Logger::undent(); + Logger::cout() << "Function type: " << tf->toChars() << '\n'; + // Logger::cout() << "LLVM functype: " << *callable->getType() << '\n'; + } - const int numFormalParams = Parameter::dim(tf->parameters); // excl. variadics - const size_t n_arguments = arguments ? arguments->dim : 0; // number of explicit arguments + const int numFormalParams = Parameter::dim(tf->parameters); // excl. variadics + const size_t n_arguments = + arguments ? arguments->dim : 0; // number of explicit arguments - std::vector argvals(n_arguments, static_cast(0)); - if (dfnval && dfnval->func->isArrayOp) { - // For array ops, the druntime implementation signatures are crafted - // specifically such that the evaluation order is as expected with - // the strange DMD reverse parameter passing order. Thus, we need - // to actually build the arguments right-to-left for them. - for (int i = numFormalParams - 1; i >= 0; --i) { - Parameter* fnarg = Parameter::getNth(tf->parameters, i); - assert(fnarg); - DValue* argval = DtoArgument(fnarg, (*arguments)[i]); - argvals[i] = argval; - } + std::vector argvals(n_arguments, static_cast(0)); + if (dfnval && dfnval->func->isArrayOp) { + // For array ops, the druntime implementation signatures are crafted + // specifically such that the evaluation order is as expected with + // the strange DMD reverse parameter passing order. Thus, we need + // to actually build the arguments right-to-left for them. + for (int i = numFormalParams - 1; i >= 0; --i) { + Parameter *fnarg = Parameter::getNth(tf->parameters, i); + assert(fnarg); + DValue *argval = DtoArgument(fnarg, (*arguments)[i]); + argvals[i] = argval; + } + } else { + for (int i = 0; i < numFormalParams; ++i) { + Parameter *fnarg = Parameter::getNth(tf->parameters, i); + assert(fnarg); + DValue *argval = DtoArgument(fnarg, (*arguments)[i]); + argvals[i] = argval; + } + } + // add varargs + for (size_t i = numFormalParams; i < n_arguments; ++i) + argvals[i] = DtoArgument(0, (*arguments)[i]); + + addExplicitArguments(args, attrs, irFty, callableTy, argvals, + numFormalParams); + + // call the function + LLCallSite call = gIR->func()->scopes->callOrInvoke(callable, args); + + // get return value + const int sretArgIndex = + (irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(tf) ? 1 + : 0); + LLValue *retllval = + (irFty.arg_sret ? args[sretArgIndex] : call.getInstruction()); + + // Hack around LDC assuming structs and static arrays are in memory: + // If the function returns a struct or a static array, and the return + // value is not a pointer to a struct or a static array, store it to + // a stack slot before continuing. + bool storeReturnValueOnStack = + (returnTy == Tstruct && !isaPointer(retllval)) || + (returnTy == Tsarray && isaArray(retllval)); + + bool retValIsAlloca = false; + + // Ignore ABI for intrinsics + const bool intrinsic = + (dfnval && dfnval->func && DtoIsIntrinsic(dfnval->func)); + if (!intrinsic && !irFty.arg_sret) { + // do abi specific return value fixups + if (storeReturnValueOnStack) { + Logger::println("Storing return value to stack slot"); + LLValue *mem = DtoAlloca(returntype); + irFty.getRet(returntype, retllval, mem); + retllval = mem; + retValIsAlloca = true; + storeReturnValueOnStack = false; } else { - for (int i = 0; i < numFormalParams; ++i) { - Parameter* fnarg = Parameter::getNth(tf->parameters, i); - assert(fnarg); - DValue* argval = DtoArgument(fnarg, (*arguments)[i]); - argvals[i] = argval; - } + retllval = irFty.getRet(returntype, retllval); + storeReturnValueOnStack = + (returnTy == Tstruct && !isaPointer(retllval)) || + (returnTy == Tsarray && isaArray(retllval)); } - // add varargs - for (size_t i = numFormalParams; i < n_arguments; ++i) - argvals[i] = DtoArgument(0, (*arguments)[i]); + } - addExplicitArguments(args, attrs, irFty, callableTy, argvals, numFormalParams); + if (storeReturnValueOnStack) { + Logger::println("Storing return value to stack slot"); + retllval = DtoAllocaDump(retllval, returntype); + retValIsAlloca = true; + } - // call the function - LLCallSite call = gIR->func()->scopes->callOrInvoke(callable, args); + // repaint the type if necessary + Type *rbase = stripModifiers(resulttype->toBasetype(), true); + Type *nextbase = stripModifiers(returntype->toBasetype(), true); + bool retinptr = irFty.arg_sret; + if (!rbase->equals(nextbase)) { + IF_LOG Logger::println("repainting return value from '%s' to '%s'", + returntype->toChars(), rbase->toChars()); + switch (rbase->ty) { + case Tarray: + if (tf->isref) + retllval = DtoBitCast(retllval, DtoType(rbase->pointerTo())); + else + retllval = DtoAggrPaint(retllval, DtoType(rbase)); + break; - // get return value - const int sretArgIndex = (irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(tf) ? 1 : 0); - LLValue* retllval = (irFty.arg_sret ? args[sretArgIndex] : call.getInstruction()); + case Tsarray: + // nothing ? + break; - // Hack around LDC assuming structs and static arrays are in memory: - // If the function returns a struct or a static array, and the return - // value is not a pointer to a struct or a static array, store it to - // a stack slot before continuing. - bool storeReturnValueOnStack = - (returnTy == Tstruct && !isaPointer(retllval)) || - (returnTy == Tsarray && isaArray(retllval)); + case Tclass: + case Taarray: + case Tpointer: + if (tf->isref) + retllval = DtoBitCast(retllval, DtoType(rbase->pointerTo())); + else + retllval = DtoBitCast(retllval, DtoType(rbase)); + break; - bool retValIsAlloca = false; + case Tstruct: + if (nextbase->ty == Taarray && !tf->isref) { + // In the D2 frontend, the associative array type and its + // object.AssociativeArray representation are used + // interchangably in some places. However, AAs are returned + // by value and not in an sret argument, so if the struct + // type will be used, give the return value storage here + // so that we get the right amount of indirections. + LLValue *val = + DtoInsertValue(llvm::UndefValue::get(DtoType(rbase)), retllval, 0); + retllval = DtoAllocaDump(val, rbase, ".aalvaluetmp"); + retinptr = true; + break; + } + // Fall through. - // Ignore ABI for intrinsics - const bool intrinsic = (dfnval && dfnval->func && DtoIsIntrinsic(dfnval->func)); - if (!intrinsic && !irFty.arg_sret) + default: + // Unfortunately, DMD has quirks resp. bugs with regard to name + // mangling: For voldemort-type functions which return a nested + // struct, the mangled name of the return type changes during + // semantic analysis. + // + // (When the function deco is first computed as part of + // determining the return type deco, its return type part is + // left off to avoid cycles. If mangle/toDecoBuffer is then + // called again for the type, it will pick up the previous + // result and return the full deco string for the nested struct + // type, consisting of both the full mangled function name, and + // the struct identifier.) + // + // Thus, the type merging in stripModifiers does not work + // reliably, and the equality check above can fail even if the + // types only differ in a qualifier. + // + // Because a proper fix for this in the frontend is hard, we + // just carry on and hope that the frontend didn't mess up, + // i.e. that the LLVM types really match up. + // + // An example situation where this case occurs is: + // --- + // auto iota() { + // static struct Result { + // this(int) {} + // inout(Result) test() inout { return cast(inout)Result(0); } + // } + // return Result.init; + // } + // void main() { auto r = iota(); } + // --- + Logger::println("Unknown return mismatch type, ignoring."); + break; + } + IF_LOG Logger::cout() << "final return value: " << *retllval << '\n'; + } + + // set calling convention and parameter attributes + llvm::AttributeSet &attrlist = attrs; + if (dfnval && dfnval->func) { + LLFunction *llfunc = llvm::dyn_cast(dfnval->val); + if (llfunc && llfunc->isIntrinsic()) // override intrinsic attrs { - // do abi specific return value fixups - if (storeReturnValueOnStack) - { - Logger::println("Storing return value to stack slot"); - LLValue* mem = DtoAlloca(returntype); - irFty.getRet(returntype, retllval, mem); - retllval = mem; - retValIsAlloca = true; - storeReturnValueOnStack = false; - } - else - { - retllval = irFty.getRet(returntype, retllval); - storeReturnValueOnStack = - (returnTy == Tstruct && !isaPointer(retllval)) || - (returnTy == Tsarray && isaArray(retllval)); - } + attrlist = llvm::Intrinsic::getAttributes( + gIR->context(), + static_cast(llfunc->getIntrinsicID())); + } else { + call.setCallingConv(callconv); } + } else { + call.setCallingConv(callconv); + } + call.setAttributes(attrlist); - if (storeReturnValueOnStack) - { - Logger::println("Storing return value to stack slot"); - retllval = DtoAllocaDump(retllval, returntype); - retValIsAlloca = true; - } + // Special case for struct constructor calls: For temporaries, using the + // this pointer value returned from the constructor instead of the alloca + // passed as a parameter (which has the same value anyway) might lead to + // instruction dominance issues because of the way it interacts with the + // cleanups (see struct ctor hack in ToElemVisitor::visit(CallExp *)). + if (dfnval && dfnval->func && dfnval->func->isCtorDeclaration() && + dfnval->func->isMember2()->isStructDeclaration()) { + return new DVarValue(resulttype, dfnval->vthis); + } - // repaint the type if necessary - Type* rbase = stripModifiers(resulttype->toBasetype(), true); - Type* nextbase = stripModifiers(returntype->toBasetype(), true); - bool retinptr = irFty.arg_sret; - if (!rbase->equals(nextbase)) - { - IF_LOG Logger::println("repainting return value from '%s' to '%s'", returntype->toChars(), rbase->toChars()); - switch(rbase->ty) - { - case Tarray: - if (tf->isref) - retllval = DtoBitCast(retllval, DtoType(rbase->pointerTo())); - else - retllval = DtoAggrPaint(retllval, DtoType(rbase)); - break; + // if we are returning through a pointer arg + // or if we are returning a reference + // make sure we provide a lvalue back! + if (retinptr || (tf->isref && returnTy != Tvoid) || retValIsAlloca) + return new DVarValue(resulttype, retllval); - case Tsarray: - // nothing ? - break; - - case Tclass: - case Taarray: - case Tpointer: - if (tf->isref) - retllval = DtoBitCast(retllval, DtoType(rbase->pointerTo())); - else - retllval = DtoBitCast(retllval, DtoType(rbase)); - break; - - case Tstruct: - if (nextbase->ty == Taarray && !tf->isref) - { - // In the D2 frontend, the associative array type and its - // object.AssociativeArray representation are used - // interchangably in some places. However, AAs are returned - // by value and not in an sret argument, so if the struct - // type will be used, give the return value storage here - // so that we get the right amount of indirections. - LLValue* val = DtoInsertValue( - llvm::UndefValue::get(DtoType(rbase)), retllval, 0); - retllval = DtoAllocaDump(val, rbase, ".aalvaluetmp"); - retinptr = true; - break; - } - // Fall through. - - default: - // Unfortunately, DMD has quirks resp. bugs with regard to name - // mangling: For voldemort-type functions which return a nested - // struct, the mangled name of the return type changes during - // semantic analysis. - // - // (When the function deco is first computed as part of - // determining the return type deco, its return type part is - // left off to avoid cycles. If mangle/toDecoBuffer is then - // called again for the type, it will pick up the previous - // result and return the full deco string for the nested struct - // type, consisting of both the full mangled function name, and - // the struct identifier.) - // - // Thus, the type merging in stripModifiers does not work - // reliably, and the equality check above can fail even if the - // types only differ in a qualifier. - // - // Because a proper fix for this in the frontend is hard, we - // just carry on and hope that the frontend didn't mess up, - // i.e. that the LLVM types really match up. - // - // An example situation where this case occurs is: - // --- - // auto iota() { - // static struct Result { - // this(int) {} - // inout(Result) test() inout { return cast(inout)Result(0); } - // } - // return Result.init; - // } - // void main() { auto r = iota(); } - // --- - Logger::println("Unknown return mismatch type, ignoring."); - break; - } - IF_LOG Logger::cout() << "final return value: " << *retllval << '\n'; - } - - // set calling convention and parameter attributes - llvm::AttributeSet& attrlist = attrs; - if (dfnval && dfnval->func) - { - LLFunction* llfunc = llvm::dyn_cast(dfnval->val); - if (llfunc && llfunc->isIntrinsic()) // override intrinsic attrs - { - attrlist = llvm::Intrinsic::getAttributes(gIR->context(), static_cast(llfunc->getIntrinsicID())); - } - else - { - call.setCallingConv(callconv); - } - } - else - { - call.setCallingConv(callconv); - } - call.setAttributes(attrlist); - - // Special case for struct constructor calls: For temporaries, using the - // this pointer value returned from the constructor instead of the alloca - // passed as a parameter (which has the same value anyway) might lead to - // instruction dominance issues because of the way it interacts with the - // cleanups (see struct ctor hack in ToElemVisitor::visit(CallExp *)). - if (dfnval && dfnval->func && dfnval->func->isCtorDeclaration() && - dfnval->func->isMember2()->isStructDeclaration()) - { - return new DVarValue(resulttype, dfnval->vthis); - } - - // if we are returning through a pointer arg - // or if we are returning a reference - // make sure we provide a lvalue back! - if (retinptr || (tf->isref && returnTy != Tvoid) || retValIsAlloca) - return new DVarValue(resulttype, retllval); - - return new DImValue(resulttype, retllval); + return new DImValue(resulttype, retllval); } diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp index 3609feedb7..e6905efa48 100644 --- a/gen/toconstelem.cpp +++ b/gen/toconstelem.cpp @@ -24,7 +24,7 @@ // Needs other includes. #include "ctfe.h" -extern dinteger_t undoStrideMul(Loc& loc, Type* t, dinteger_t offset); +extern dinteger_t undoStrideMul(Loc &loc, Type *t, dinteger_t offset); /// Emits an LLVM constant corresponding to the expression. /// @@ -32,281 +32,265 @@ extern dinteger_t undoStrideMul(Loc& loc, Type* t, dinteger_t offset); /// implementations have to be able to handle being called on expressions /// that are not actually constant. In such a case, an LLVM undef of the /// expected type should be returned (_not_ null). -class ToConstElemVisitor : public Visitor -{ - IRState *p; - LLConstant *result; +class ToConstElemVisitor : public Visitor { + IRState *p; + LLConstant *result; + public: + ToConstElemVisitor(IRState *p_) : p(p_) {} - ToConstElemVisitor(IRState *p_) : p(p_) { } + // Import all functions from class Visitor + using Visitor::visit; - // Import all functions from class Visitor - using Visitor::visit; + ////////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////// + LLConstant *toConstElem(Expression *e) { + result = 0; + e->accept(this); + return result; + } - LLConstant *toConstElem(Expression *e) - { - result = 0; - e->accept(this); - return result; + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(VarExp *e) { + IF_LOG Logger::print("VarExp::toConstElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + if (SymbolDeclaration *sdecl = e->var->isSymbolDeclaration()) { + // this seems to be the static initialiser for structs + Type *sdecltype = sdecl->type->toBasetype(); + IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); + assert(sdecltype->ty == Tstruct); + TypeStruct *ts = static_cast(sdecltype); + DtoResolveStruct(ts->sym); + result = getIrAggr(ts->sym)->getDefaultInit(); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(VarExp *e) - { - IF_LOG Logger::print("VarExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - if (SymbolDeclaration* sdecl = e->var->isSymbolDeclaration()) - { - // this seems to be the static initialiser for structs - Type* sdecltype = sdecl->type->toBasetype(); - IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); - assert(sdecltype->ty == Tstruct); - TypeStruct* ts = static_cast(sdecltype); - DtoResolveStruct(ts->sym); - result = getIrAggr(ts->sym)->getDefaultInit(); - return; - } - - if (TypeInfoDeclaration* ti = e->var->isTypeInfoDeclaration()) - { - LLType* vartype = DtoType(e->type); - result = DtoTypeInfoOf(ti->tinfo, false); - if (result->getType() != getPtrToType(vartype)) - result = llvm::ConstantExpr::getBitCast(result, vartype); - return; - } - - VarDeclaration* vd = e->var->isVarDeclaration(); - if (vd && vd->isConst() && vd->init) - { - if (vd->inuse) - { - e->error("recursive reference %s", e->toChars()); - result = llvm::UndefValue::get(DtoType(e->type)); - } - else - { - vd->inuse++; - // return the initializer - result = DtoConstInitializer(e->loc, e->type, vd->init); - vd->inuse--; - } - } - // fail - else - { - e->error("non-constant expression %s", e->toChars()); - result = llvm::UndefValue::get(DtoType(e->type)); - } + if (TypeInfoDeclaration *ti = e->var->isTypeInfoDeclaration()) { + LLType *vartype = DtoType(e->type); + result = DtoTypeInfoOf(ti->tinfo, false); + if (result->getType() != getPtrToType(vartype)) + result = llvm::ConstantExpr::getBitCast(result, vartype); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + VarDeclaration *vd = e->var->isVarDeclaration(); + if (vd && vd->isConst() && vd->init) { + if (vd->inuse) { + e->error("recursive reference %s", e->toChars()); + result = llvm::UndefValue::get(DtoType(e->type)); + } else { + vd->inuse++; + // return the initializer + result = DtoConstInitializer(e->loc, e->type, vd->init); + vd->inuse--; + } + } + // fail + else { + e->error("non-constant expression %s", e->toChars()); + result = llvm::UndefValue::get(DtoType(e->type)); + } + } - void visit(IntegerExp *e) - { - IF_LOG Logger::print("IntegerExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - LLType* t = DtoType(e->type); - if (isaPointer(t)) - { - Logger::println("pointer"); - LLConstant* i = LLConstantInt::get(DtoSize_t(), (uint64_t)e->getInteger(), false); - result = llvm::ConstantExpr::getIntToPtr(i, t); - } - else - { - assert(llvm::isa(t)); - result = LLConstantInt::get(t, (uint64_t)e->getInteger(), !e->type->isunsigned()); - assert(result); - IF_LOG Logger::cout() << "value = " << *result << '\n'; - } + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(IntegerExp *e) { + IF_LOG Logger::print("IntegerExp::toConstElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + LLType *t = DtoType(e->type); + if (isaPointer(t)) { + Logger::println("pointer"); + LLConstant *i = + LLConstantInt::get(DtoSize_t(), (uint64_t)e->getInteger(), false); + result = llvm::ConstantExpr::getIntToPtr(i, t); + } else { + assert(llvm::isa(t)); + result = LLConstantInt::get(t, (uint64_t)e->getInteger(), + !e->type->isunsigned()); + assert(result); + IF_LOG Logger::cout() << "value = " << *result << '\n'; + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(RealExp *e) { + IF_LOG Logger::print("RealExp::toConstElem: %s @ %s | %La\n", e->toChars(), + e->type->toChars(), e->value); + LOG_SCOPE; + Type *t = e->type->toBasetype(); + result = DtoConstFP(t, e->value); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(NullExp *e) { + IF_LOG Logger::print("NullExp::toConstElem(type=%s): %s\n", + e->type->toChars(), e->toChars()); + LOG_SCOPE; + LLType *t = DtoType(e->type); + if (e->type->ty == Tarray) { + assert(isaStruct(t)); + result = llvm::ConstantAggregateZero::get(t); + } else { + result = LLConstant::getNullValue(t); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(ComplexExp *e) { + IF_LOG Logger::print("ComplexExp::toConstElem(): %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + result = DtoConstComplex(e->type, e->value.re, e->value.im); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(StringExp *e) { + IF_LOG Logger::print("StringExp::toConstElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + Type *t = e->type->toBasetype(); + Type *cty = t->nextOf()->toBasetype(); + + bool nullterm = (t->ty != Tsarray); + size_t endlen = nullterm ? e->len + 1 : e->len; + + LLType *ct = DtoMemType(cty); + LLArrayType *at = LLArrayType::get(ct, endlen); + + llvm::StringMap *stringLiteralCache = 0; + LLConstant *_init; + switch (cty->size()) { + default: + llvm_unreachable("Unknown char type"); + case 1: + _init = toConstantArray(ct, at, static_cast(e->string), e->len, + nullterm); + stringLiteralCache = &(gIR->stringLiteral1ByteCache); + break; + case 2: + _init = toConstantArray(ct, at, static_cast(e->string), + e->len, nullterm); + stringLiteralCache = &(gIR->stringLiteral2ByteCache); + break; + case 4: + _init = toConstantArray(ct, at, static_cast(e->string), + e->len, nullterm); + stringLiteralCache = &(gIR->stringLiteral4ByteCache); + break; } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(RealExp *e) - { - IF_LOG Logger::print("RealExp::toConstElem: %s @ %s | %La\n", e->toChars(), e->type->toChars(), e->value); - LOG_SCOPE; - Type* t = e->type->toBasetype(); - result = DtoConstFP(t, e->value); + if (t->ty == Tsarray) { + result = _init; + return; } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(NullExp *e) - { - IF_LOG Logger::print("NullExp::toConstElem(type=%s): %s\n", e->type->toChars(), e->toChars()); - LOG_SCOPE; - LLType* t = DtoType(e->type); - if (e->type->ty == Tarray) - { - assert(isaStruct(t)); - result = llvm::ConstantAggregateZero::get(t); - } - else - { - result = LLConstant::getNullValue(t); - } + llvm::StringRef key(e->toChars()); + llvm::GlobalVariable *gvar = + (stringLiteralCache->find(key) == stringLiteralCache->end()) + ? 0 + : (*stringLiteralCache)[key]; + if (gvar == 0) { + llvm::GlobalValue::LinkageTypes _linkage = + llvm::GlobalValue::PrivateLinkage; + gvar = new llvm::GlobalVariable(gIR->module, _init->getType(), true, + _linkage, _init, ".str"); + gvar->setUnnamedAddr(true); + (*stringLiteralCache)[key] = gvar; } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(ComplexExp *e) - { - IF_LOG Logger::print("ComplexExp::toConstElem(): %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - result = DtoConstComplex(e->type, e->value.re, e->value.im); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(StringExp *e) - { - IF_LOG Logger::print("StringExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - Type* t = e->type->toBasetype(); - Type* cty = t->nextOf()->toBasetype(); - - bool nullterm = (t->ty != Tsarray); - size_t endlen = nullterm ? e->len+1 : e->len; - - LLType* ct = DtoMemType(cty); - LLArrayType* at = LLArrayType::get(ct,endlen); - - llvm::StringMap* stringLiteralCache = 0; - LLConstant* _init; - switch (cty->size()) - { - default: - llvm_unreachable("Unknown char type"); - case 1: - _init = toConstantArray(ct, at, static_cast(e->string), e->len, nullterm); - stringLiteralCache = &(gIR->stringLiteral1ByteCache); - break; - case 2: - _init = toConstantArray(ct, at, static_cast(e->string), e->len, nullterm); - stringLiteralCache = &(gIR->stringLiteral2ByteCache); - break; - case 4: - _init = toConstantArray(ct, at, static_cast(e->string), e->len, nullterm); - stringLiteralCache = &(gIR->stringLiteral4ByteCache); - break; - } - - if (t->ty == Tsarray) - { - result = _init; - return; - } - - llvm::StringRef key(e->toChars()); - llvm::GlobalVariable* gvar = (stringLiteralCache->find(key) == - stringLiteralCache->end()) - ? 0 : (*stringLiteralCache)[key]; - if (gvar == 0) - { - llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::PrivateLinkage; - gvar = new llvm::GlobalVariable(gIR->module, _init->getType(), true, _linkage, _init, ".str"); - gvar->setUnnamedAddr(true); - (*stringLiteralCache)[key] = gvar; - } - - llvm::ConstantInt* zero = LLConstantInt::get(LLType::getInt32Ty(gIR->context()), 0, false); - LLConstant* idxs[2] = { zero, zero }; + llvm::ConstantInt *zero = + LLConstantInt::get(LLType::getInt32Ty(gIR->context()), 0, false); + LLConstant *idxs[2] = {zero, zero}; #if LDC_LLVM_VER >= 307 - LLConstant* arrptr = llvm::ConstantExpr::getGetElementPtr(isaPointer(gvar)->getElementType(), gvar, idxs, true); + LLConstant *arrptr = llvm::ConstantExpr::getGetElementPtr( + isaPointer(gvar)->getElementType(), gvar, idxs, true); #else - LLConstant* arrptr = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true); + LLConstant *arrptr = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true); #endif - if (t->ty == Tpointer) - { - result = arrptr; - } - else if (t->ty == Tarray) - { - LLConstant* clen = LLConstantInt::get(DtoSize_t(), e->len, false); - result = DtoConstSlice(clen, arrptr, e->type); - } - else - { - llvm_unreachable("Unknown type for StringExp."); - } + if (t->ty == Tpointer) { + result = arrptr; + } else if (t->ty == Tarray) { + LLConstant *clen = LLConstantInt::get(DtoSize_t(), e->len, false); + result = DtoConstSlice(clen, arrptr, e->type); + } else { + llvm_unreachable("Unknown type for StringExp."); } + } - ////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// - void visit(AddExp *e) - { - IF_LOG Logger::print("AddExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + void visit(AddExp *e) { + IF_LOG Logger::print("AddExp::toConstElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // add to pointer - Type* t1b = e->e1->type->toBasetype(); - if (t1b->ty == Tpointer && e->e2->type->isintegral()) - { - llvm::Constant* ptr = toConstElem(e->e1); - dinteger_t idx = undoStrideMul(e->loc, t1b, e->e2->toInteger()); + // add to pointer + Type *t1b = e->e1->type->toBasetype(); + if (t1b->ty == Tpointer && e->e2->type->isintegral()) { + llvm::Constant *ptr = toConstElem(e->e1); + dinteger_t idx = undoStrideMul(e->loc, t1b, e->e2->toInteger()); #if LDC_LLVM_VER >= 307 - result = llvm::ConstantExpr::getGetElementPtr(isaPointer(ptr)->getElementType(), ptr, DtoConstSize_t(idx)); + result = llvm::ConstantExpr::getGetElementPtr( + isaPointer(ptr)->getElementType(), ptr, DtoConstSize_t(idx)); #else - result = llvm::ConstantExpr::getGetElementPtr(ptr, DtoConstSize_t(idx)); + result = llvm::ConstantExpr::getGetElementPtr(ptr, DtoConstSize_t(idx)); #endif - } - else - { - e->error("expression '%s' is not a constant", e->toChars()); - if (!global.gag) fatal(); - result = llvm::UndefValue::get(DtoType(e->type)); - } + } else { + e->error("expression '%s' is not a constant", e->toChars()); + if (!global.gag) + fatal(); + result = llvm::UndefValue::get(DtoType(e->type)); } + } - void visit(MinExp *e) - { - IF_LOG Logger::print("MinExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + void visit(MinExp *e) { + IF_LOG Logger::print("MinExp::toConstElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - Type* t1b = e->e1->type->toBasetype(); - if (t1b->ty == Tpointer && e->e2->type->isintegral()) - { - llvm::Constant* ptr = toConstElem(e->e1); - dinteger_t idx = undoStrideMul(e->loc, t1b, e->e2->toInteger()); + Type *t1b = e->e1->type->toBasetype(); + if (t1b->ty == Tpointer && e->e2->type->isintegral()) { + llvm::Constant *ptr = toConstElem(e->e1); + dinteger_t idx = undoStrideMul(e->loc, t1b, e->e2->toInteger()); - llvm::Constant* negIdx = llvm::ConstantExpr::getNeg(DtoConstSize_t(idx)); + llvm::Constant *negIdx = llvm::ConstantExpr::getNeg(DtoConstSize_t(idx)); #if LDC_LLVM_VER >= 307 - result = llvm::ConstantExpr::getGetElementPtr(isaPointer(ptr)->getElementType(), ptr, negIdx); + result = llvm::ConstantExpr::getGetElementPtr( + isaPointer(ptr)->getElementType(), ptr, negIdx); #else - result = llvm::ConstantExpr::getGetElementPtr(ptr, negIdx); + result = llvm::ConstantExpr::getGetElementPtr(ptr, negIdx); #endif - } - else - { - e->error("expression '%s' is not a constant", e->toChars()); - if (!global.gag) fatal(); - result = llvm::UndefValue::get(DtoType(e->type)); - } + } else { + e->error("expression '%s' is not a constant", e->toChars()); + if (!global.gag) + fatal(); + result = llvm::UndefValue::get(DtoType(e->type)); } + } - ////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// - void visit(CastExp *e) - { - IF_LOG Logger::print("CastExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + void visit(CastExp *e) { + IF_LOG Logger::print("CastExp::toConstElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - LLType* lltype = DtoType(e->type); - Type* tb = e->to->toBasetype(); + LLType *lltype = DtoType(e->type); + Type *tb = e->to->toBasetype(); - // string literal to dyn array: - // reinterpret the string data as an array, calculate the length - if (e->e1->op == TOKstring && tb->ty == Tarray) - { + // string literal to dyn array: + // reinterpret the string data as an array, calculate the length + if (e->e1->op == TOKstring && tb->ty == Tarray) { #if 0 StringExp *strexp = static_cast(e1); size_t datalen = strexp->sz * strexp->len; @@ -317,493 +301,468 @@ public: } size_t arrlen = datalen / eltype->size(); #endif - e->error("ct cast of string to dynamic array not fully implemented"); - result = toConstElem(e->e1); - } - // pointer to pointer - else if (tb->ty == Tpointer && e->e1->type->toBasetype()->ty == Tpointer) - { - result = llvm::ConstantExpr::getBitCast(toConstElem(e->e1), lltype); - } - // global variable to pointer - else if (tb->ty == Tpointer && e->e1->op == TOKvar) - { - VarDeclaration *vd = static_cast(e->e1)->var->isVarDeclaration(); - assert(vd); - DtoResolveVariable(vd); - LLConstant *value = isIrGlobalCreated(vd) ? isaConstant(getIrGlobal(vd)->value) : 0; - if (!value) - goto Lerr; - Type *type = vd->type->toBasetype(); - if (type->ty == Tarray || type->ty == Tdelegate) { - LLConstant* idxs[2] = { DtoConstSize_t(0), DtoConstSize_t(1) }; + e->error("ct cast of string to dynamic array not fully implemented"); + result = toConstElem(e->e1); + } + // pointer to pointer + else if (tb->ty == Tpointer && e->e1->type->toBasetype()->ty == Tpointer) { + result = llvm::ConstantExpr::getBitCast(toConstElem(e->e1), lltype); + } + // global variable to pointer + else if (tb->ty == Tpointer && e->e1->op == TOKvar) { + VarDeclaration *vd = + static_cast(e->e1)->var->isVarDeclaration(); + assert(vd); + DtoResolveVariable(vd); + LLConstant *value = + isIrGlobalCreated(vd) ? isaConstant(getIrGlobal(vd)->value) : 0; + if (!value) + goto Lerr; + Type *type = vd->type->toBasetype(); + if (type->ty == Tarray || type->ty == Tdelegate) { + LLConstant *idxs[2] = {DtoConstSize_t(0), DtoConstSize_t(1)}; #if LDC_LLVM_VER >= 307 - value = llvm::ConstantExpr::getGetElementPtr(isaPointer(value)->getElementType(), value, idxs, true); + value = llvm::ConstantExpr::getGetElementPtr( + isaPointer(value)->getElementType(), value, idxs, true); #else - value = llvm::ConstantExpr::getGetElementPtr(value, idxs, true); + value = llvm::ConstantExpr::getGetElementPtr(value, idxs, true); #endif - } - result = DtoBitCast(value, DtoType(tb)); - } - else if (tb->ty == Tclass && e->e1->type->ty == Tclass) - { - assert(e->e1->op == TOKclassreference); - ClassDeclaration* cd = static_cast(e->e1)->originalClass(); + } + result = DtoBitCast(value, DtoType(tb)); + } else if (tb->ty == Tclass && e->e1->type->ty == Tclass) { + assert(e->e1->op == TOKclassreference); + ClassDeclaration *cd = + static_cast(e->e1)->originalClass(); - llvm::Constant* instance = toConstElem(e->e1); - if (InterfaceDeclaration* it = static_cast(tb)->sym->isInterfaceDeclaration()) { - assert(it->isBaseOf(cd, NULL)); + llvm::Constant *instance = toConstElem(e->e1); + if (InterfaceDeclaration *it = + static_cast(tb)->sym->isInterfaceDeclaration()) { + assert(it->isBaseOf(cd, NULL)); - IrTypeClass* typeclass = cd->type->ctype->isClass(); + IrTypeClass *typeclass = cd->type->ctype->isClass(); - // find interface impl - size_t i_index = typeclass->getInterfaceIndex(it); - assert(i_index != ~0UL); + // find interface impl + size_t i_index = typeclass->getInterfaceIndex(it); + assert(i_index != ~0UL); - // offset pointer - instance = DtoGEPi(instance, 0, i_index); - } - result = DtoBitCast(instance, DtoType(tb)); - } - else - { - goto Lerr; - } + // offset pointer + instance = DtoGEPi(instance, 0, i_index); + } + result = DtoBitCast(instance, DtoType(tb)); + } else { + goto Lerr; + } + return; + + Lerr: + e->error("cannot cast %s to %s at compile time", e->e1->type->toChars(), + e->type->toChars()); + if (!global.gag) + fatal(); + result = llvm::UndefValue::get(DtoType(e->type)); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(SymOffExp *e) { + IF_LOG Logger::println("SymOffExp::toConstElem: %s @ %s", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + llvm::Constant *base = DtoConstSymbolAddress(e->loc, e->var); + if (base == 0) { + result = llvm::UndefValue::get(DtoType(e->type)); + return; + } + + if (e->offset == 0) { + result = base; + } else { + const unsigned elemSize = + gDataLayout->getTypeStoreSize(base->getType()->getContainedType(0)); + + IF_LOG Logger::println("adding offset: %llu (elem size: %u)", + static_cast(e->offset), + elemSize); + + if (e->offset % elemSize == 0) { + // We can turn this into a "nice" GEP. + result = llvm::ConstantExpr::getGetElementPtr( +#if LDC_LLVM_VER >= 307 + NULL, +#endif + base, DtoConstSize_t(e->offset / elemSize)); + } else { + // Offset isn't a multiple of base type size, just cast to i8* and + // apply the byte offset. + result = llvm::ConstantExpr::getGetElementPtr( +#if LDC_LLVM_VER >= 307 + NULL, +#endif + DtoBitCast(base, getVoidPtrType()), DtoConstSize_t(e->offset)); + } + } + + result = DtoBitCast(result, DtoType(e->type)); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(AddrExp *e) { + IF_LOG Logger::println("AddrExp::toConstElem: %s @ %s", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + // FIXME: this should probably be generalized more so we don't + // need to have a case for each thing we can take the address of + + // address of global variable + if (e->e1->op == TOKvar) { + VarExp *vexp = static_cast(e->e1); + LLConstant *c = DtoConstSymbolAddress(e->loc, vexp->var); + result = c ? DtoBitCast(c, DtoType(e->type)) : 0; + } + // address of indexExp + else if (e->e1->op == TOKindex) { + IndexExp *iexp = static_cast(e->e1); + + // indexee must be global static array var + assert(iexp->e1->op == TOKvar); + VarExp *vexp = static_cast(iexp->e1); + VarDeclaration *vd = vexp->var->isVarDeclaration(); + assert(vd); + assert(vd->type->toBasetype()->ty == Tsarray); + DtoResolveVariable(vd); + assert(isIrGlobalCreated(vd)); + + // get index + LLConstant *index = toConstElem(iexp->e2); + assert(index->getType() == DtoSize_t()); + + // gep + LLConstant *idxs[2] = {DtoConstSize_t(0), index}; + LLConstant *val = isaConstant(getIrGlobal(vd)->value); + val = DtoBitCast(val, DtoType(vd->type->pointerTo())); +#if LDC_LLVM_VER >= 307 + LLConstant *gep = llvm::ConstantExpr::getGetElementPtr( + isaPointer(val)->getElementType(), val, idxs, true); +#else + LLConstant *gep = llvm::ConstantExpr::getGetElementPtr(val, idxs, true); +#endif + + // bitcast to requested type + assert(e->type->toBasetype()->ty == Tpointer); + result = DtoBitCast(gep, DtoType(e->type)); + } else if (e->e1->op == TOKstructliteral) { + StructLiteralExp *se = static_cast(e->e1); + + if (se->globalVar) { + IF_LOG Logger::cout() << "Returning existing global: " << *se->globalVar + << '\n'; + result = se->globalVar; return; + } - Lerr: - e->error("cannot cast %s to %s at compile time", e->e1->type->toChars(), e->type->toChars()); - if (!global.gag) - fatal(); - result = llvm::UndefValue::get(DtoType(e->type)); + se->globalVar = new llvm::GlobalVariable( + p->module, DtoType(e->e1->type), false, + llvm::GlobalValue::InternalLinkage, 0, ".structliteral"); + + llvm::Constant *constValue = toConstElem(se); + if (constValue->getType() != + se->globalVar->getType()->getContainedType(0)) { + llvm::GlobalVariable *finalGlobalVar = new llvm::GlobalVariable( + p->module, constValue->getType(), false, + llvm::GlobalValue::InternalLinkage, 0, ".structliteral"); + se->globalVar->replaceAllUsesWith( + DtoBitCast(finalGlobalVar, se->globalVar->getType())); + se->globalVar->eraseFromParent(); + se->globalVar = finalGlobalVar; + } + se->globalVar->setInitializer(constValue); + se->globalVar->setAlignment(DtoAlignment(se->type)); + + result = se->globalVar; + } else if (e->e1->op == TOKslice) { + e->error("non-constant expression '%s'", e->toChars()); + if (!global.gag) + fatal(); + result = llvm::UndefValue::get(DtoType(e->type)); + } + // not yet supported + else { + e->error("constant expression '%s' not yet implemented", e->toChars()); + fatal(); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(FuncExp *e) { + IF_LOG Logger::print("FuncExp::toConstElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + FuncLiteralDeclaration *fd = e->fd; + assert(fd); + + if (fd->tok == TOKreserved && e->type->ty == Tpointer) { + // This is a lambda that was inferred to be a function literal instead + // of a delegate, so set tok here in order to get correct types/mangling. + // Horrible hack, but DMD does the same thing in FuncExp::toElem and + // other random places. + fd->tok = TOKfunction; + fd->vthis = NULL; } - ////////////////////////////////////////////////////////////////////////////////////////// + if (fd->tok != TOKfunction) { + assert(fd->tok == TOKdelegate || fd->tok == TOKreserved); + e->error("non-constant nested delegate literal expression %s", + e->toChars()); + if (!global.gag) + fatal(); + result = llvm::UndefValue::get(DtoType(e->type)); + } else { + // We need to actually codegen the function here, as literals are not + // added + // to the module member list. + Declaration_codegen(fd, p); + assert(getIrFunc(fd)->func); - void visit(SymOffExp *e) - { - IF_LOG Logger::println("SymOffExp::toConstElem: %s @ %s", e->toChars(), e->type->toChars()); - LOG_SCOPE; + result = getIrFunc(fd)->func; + } + } - llvm::Constant* base = DtoConstSymbolAddress(e->loc, e->var); - if (base == 0) - { - result = llvm::UndefValue::get(DtoType(e->type)); - return; - } + ////////////////////////////////////////////////////////////////////////////////////////// - if (e->offset == 0) - { - result = base; - } - else - { - const unsigned elemSize = gDataLayout->getTypeStoreSize( - base->getType()->getContainedType(0)); + void visit(ArrayLiteralExp *e) { + IF_LOG Logger::print("ArrayLiteralExp::toConstElem: %s @ %s\n", + e->toChars(), e->type->toChars()); + LOG_SCOPE; - IF_LOG Logger::println("adding offset: %llu (elem size: %u)", static_cast(e->offset), elemSize); + // extract D types + Type *bt = e->type->toBasetype(); + Type *elemt = bt->nextOf(); - if (e->offset % elemSize == 0) - { - // We can turn this into a "nice" GEP. - result = llvm::ConstantExpr::getGetElementPtr( -#if LDC_LLVM_VER >= 307 - NULL, -#endif - base, - DtoConstSize_t(e->offset / elemSize)); - } - else - { - // Offset isn't a multiple of base type size, just cast to i8* and - // apply the byte offset. - result = llvm::ConstantExpr::getGetElementPtr( -#if LDC_LLVM_VER >= 307 - NULL, -#endif - DtoBitCast(base, getVoidPtrType()), - DtoConstSize_t(e->offset)); - } - } + // build llvm array type + LLArrayType *arrtype = + LLArrayType::get(DtoMemType(elemt), e->elements->dim); - result = DtoBitCast(result, DtoType(e->type)); + // dynamic arrays can occur here as well ... + bool dyn = (bt->ty != Tsarray); + + llvm::Constant *initval = arrayLiteralToConst(p, e); + + // if static array, we're done + if (!dyn) { + result = initval; + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + bool canBeConst = e->type->isConst() || e->type->isImmutable(); + llvm::GlobalVariable *gvar = new llvm::GlobalVariable( + gIR->module, initval->getType(), canBeConst, + llvm::GlobalValue::InternalLinkage, initval, ".dynarrayStorage"); + gvar->setUnnamedAddr(canBeConst); + llvm::Constant *store = DtoBitCast(gvar, getPtrToType(arrtype)); - void visit(AddrExp *e) - { - IF_LOG Logger::println("AddrExp::toConstElem: %s @ %s", e->toChars(), e->type->toChars()); - LOG_SCOPE; - // FIXME: this should probably be generalized more so we don't - // need to have a case for each thing we can take the address of + if (bt->ty == Tpointer) { + // we need to return pointer to the static array. + result = store; + return; + } - // address of global variable - if (e->e1->op == TOKvar) - { - VarExp* vexp = static_cast(e->e1); - LLConstant *c = DtoConstSymbolAddress(e->loc, vexp->var); - result = c ? DtoBitCast(c, DtoType(e->type)) : 0; - } - // address of indexExp - else if (e->e1->op == TOKindex) - { - IndexExp* iexp = static_cast(e->e1); - - // indexee must be global static array var - assert(iexp->e1->op == TOKvar); - VarExp* vexp = static_cast(iexp->e1); - VarDeclaration* vd = vexp->var->isVarDeclaration(); - assert(vd); - assert(vd->type->toBasetype()->ty == Tsarray); - DtoResolveVariable(vd); - assert(isIrGlobalCreated(vd)); - - // get index - LLConstant* index = toConstElem(iexp->e2); - assert(index->getType() == DtoSize_t()); - - // gep - LLConstant* idxs[2] = { DtoConstSize_t(0), index }; - LLConstant *val = isaConstant(getIrGlobal(vd)->value); - val = DtoBitCast(val, DtoType(vd->type->pointerTo())); + // build a constant dynamic array reference with the .ptr field pointing + // into store + LLConstant *idxs[2] = {DtoConstUint(0), DtoConstUint(0)}; #if LDC_LLVM_VER >= 307 - LLConstant* gep = llvm::ConstantExpr::getGetElementPtr(isaPointer(val)->getElementType(), val, idxs, true); + LLConstant *globalstorePtr = llvm::ConstantExpr::getGetElementPtr( + isaPointer(store)->getElementType(), store, idxs, true); #else - LLConstant* gep = llvm::ConstantExpr::getGetElementPtr(val, idxs, true); + LLConstant *globalstorePtr = + llvm::ConstantExpr::getGetElementPtr(store, idxs, true); #endif - // bitcast to requested type - assert(e->type->toBasetype()->ty == Tpointer); - result = DtoBitCast(gep, DtoType(e->type)); - } - else if (e->e1->op == TOKstructliteral) - { - StructLiteralExp* se = static_cast(e->e1); + result = DtoConstSlice(DtoConstSize_t(e->elements->dim), globalstorePtr); + } - if (se->globalVar) - { - IF_LOG Logger::cout() << "Returning existing global: " << *se->globalVar << '\n'; - result = se->globalVar; - return; + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(StructLiteralExp *e) { + // type can legitimately be null for ClassReferenceExp::value. + IF_LOG Logger::print("StructLiteralExp::toConstElem: %s @ %s\n", + e->toChars(), e->type ? e->type->toChars() : "(null)"); + LOG_SCOPE; + + if (e->sinit) { + // Copied from VarExp::toConstElem, need to clean this mess up. + Type *sdecltype = e->sinit->type->toBasetype(); + IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); + assert(sdecltype->ty == Tstruct); + TypeStruct *ts = static_cast(sdecltype); + DtoResolveStruct(ts->sym); + + result = getIrAggr(ts->sym)->getDefaultInit(); + } else { + // make sure the struct is resolved + DtoResolveStruct(e->sd); + + std::map varInits; + const size_t nexprs = e->elements->dim; + for (size_t i = 0; i < nexprs; i++) { + if ((*e->elements)[i]) { + varInits[e->sd->fields[i]] = toConstElem((*e->elements)[i]); + } + } + + result = getIrAggr(e->sd)->createInitializerConstant(varInits); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(ClassReferenceExp *e) { + IF_LOG Logger::print("ClassReferenceExp::toConstElem: %s @ %s\n", + e->toChars(), e->type->toChars()); + LOG_SCOPE; + + ClassDeclaration *origClass = e->originalClass(); + DtoResolveClass(origClass); + StructLiteralExp *value = e->value; + + if (value->globalVar) { + IF_LOG Logger::cout() << "Using existing global: " << *value->globalVar + << '\n'; + } else { + value->globalVar = new llvm::GlobalVariable( + p->module, origClass->type->ctype->isClass()->getMemoryLLType(), + false, llvm::GlobalValue::InternalLinkage, 0, ".classref"); + + std::map varInits; + + // Unfortunately, ClassReferenceExp::getFieldAt is badly broken – it + // places the base class fields _after_ those of the subclass. + { + const size_t nexprs = value->elements->dim; + + std::stack classHierachy; + ClassDeclaration *cur = origClass; + while (cur) { + classHierachy.push(cur); + cur = cur->baseClass; + } + size_t i = 0; + while (!classHierachy.empty()) { + cur = classHierachy.top(); + classHierachy.pop(); + for (size_t j = 0; j < cur->fields.dim; ++j) { + if ((*value->elements)[i]) { + VarDeclaration *field = cur->fields[j]; + IF_LOG Logger::println("Getting initializer for: %s", + field->toChars()); + LOG_SCOPE; + varInits[field] = toConstElem((*value->elements)[i]); } - - se->globalVar = new llvm::GlobalVariable(p->module, - DtoType(e->e1->type), false, llvm::GlobalValue::InternalLinkage, 0, - ".structliteral"); - - llvm::Constant* constValue = toConstElem(se); - if (constValue->getType() != se->globalVar->getType()->getContainedType(0)) - { - llvm::GlobalVariable* finalGlobalVar = new llvm::GlobalVariable( - p->module, constValue->getType(), false, - llvm::GlobalValue::InternalLinkage, 0, ".structliteral"); - se->globalVar->replaceAllUsesWith( - DtoBitCast(finalGlobalVar, se->globalVar->getType())); - se->globalVar->eraseFromParent(); - se->globalVar = finalGlobalVar; - } - se->globalVar->setInitializer(constValue); - se->globalVar->setAlignment(DtoAlignment(se->type)); - - result = se->globalVar; - } - else if (e->e1->op == TOKslice) - { - e->error("non-constant expression '%s'", e->toChars()); - if (!global.gag) fatal(); - result = llvm::UndefValue::get(DtoType(e->type)); - } - // not yet supported - else - { - e->error("constant expression '%s' not yet implemented", e->toChars()); - fatal(); + ++i; + } } + assert(i == nexprs); + } + + llvm::Constant *constValue = + getIrAggr(origClass)->createInitializerConstant(varInits); + + if (constValue->getType() != + value->globalVar->getType()->getContainedType(0)) { + llvm::GlobalVariable *finalGlobalVar = new llvm::GlobalVariable( + p->module, constValue->getType(), false, + llvm::GlobalValue::InternalLinkage, 0, ".classref"); + value->globalVar->replaceAllUsesWith( + DtoBitCast(finalGlobalVar, value->globalVar->getType())); + value->globalVar->eraseFromParent(); + value->globalVar = finalGlobalVar; + } + value->globalVar->setInitializer(constValue); } - ////////////////////////////////////////////////////////////////////////////////////////// + result = value->globalVar; - void visit(FuncExp *e) - { - IF_LOG Logger::print("FuncExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + if (e->type->ty == Tclass) { + ClassDeclaration *targetClass = static_cast(e->type)->sym; + if (InterfaceDeclaration *it = targetClass->isInterfaceDeclaration()) { + assert(it->isBaseOf(origClass, NULL)); - FuncLiteralDeclaration *fd = e->fd; - assert(fd); + IrTypeClass *typeclass = origClass->type->ctype->isClass(); - if (fd->tok == TOKreserved && e->type->ty == Tpointer) - { - // This is a lambda that was inferred to be a function literal instead - // of a delegate, so set tok here in order to get correct types/mangling. - // Horrible hack, but DMD does the same thing in FuncExp::toElem and - // other random places. - fd->tok = TOKfunction; - fd->vthis = NULL; - } + // find interface impl + size_t i_index = typeclass->getInterfaceIndex(it); + assert(i_index != ~0UL); - if (fd->tok != TOKfunction) - { - assert(fd->tok == TOKdelegate || fd->tok == TOKreserved); - e->error("non-constant nested delegate literal expression %s", e->toChars()); - if (!global.gag) fatal(); - result = llvm::UndefValue::get(DtoType(e->type)); - } - else - { - // We need to actually codegen the function here, as literals are not added - // to the module member list. - Declaration_codegen(fd, p); - assert(getIrFunc(fd)->func); - - result = getIrFunc(fd)->func; - } + // offset pointer + result = DtoGEPi(result, 0, i_index); + } } - ////////////////////////////////////////////////////////////////////////////////////////// + assert(e->type->ty == Tclass || e->type->ty == Tenum); + result = DtoBitCast(result, DtoType(e->type)); + } - void visit(ArrayLiteralExp *e) - { - IF_LOG Logger::print("ArrayLiteralExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////////////////////// - // extract D types - Type* bt = e->type->toBasetype(); - Type* elemt = bt->nextOf(); + void visit(VectorExp *e) { + IF_LOG Logger::print("VectorExp::toConstElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // build llvm array type - LLArrayType* arrtype = LLArrayType::get(DtoMemType(elemt), e->elements->dim); + TypeVector *tv = static_cast(e->to->toBasetype()); + assert(tv->ty == Tvector); - // dynamic arrays can occur here as well ... - bool dyn = (bt->ty != Tsarray); + // The AST for + // static immutable ubyte16 vec1 = 123; + // differs from + // static immutable ubyte[16] vec1 = 123; + // In the vector case the AST contains an IntegerExp (of type int) and a + // CastExp to type ubyte. In the static array case the AST only contains an + // IntegerExp of type ubyte. Simply call optimize to get rid of the cast. + // FIXME: Check DMD source to understand why two different ASTs are + // constructed. + llvm::Constant *val = toConstElem(e->e1->optimize(WANTvalue)); - llvm::Constant* initval = arrayLiteralToConst(p, e); + dinteger_t elemCount = + static_cast(tv->basetype)->dim->toInteger(); + result = llvm::ConstantVector::getSplat(elemCount, val); + } - // if static array, we're done - if (!dyn) - { - result = initval; - return; - } + ////////////////////////////////////////////////////////////////////////////////////////// - bool canBeConst = e->type->isConst() || e->type->isImmutable(); - llvm::GlobalVariable* gvar = new llvm::GlobalVariable(gIR->module, - initval->getType(), canBeConst, llvm::GlobalValue::InternalLinkage, initval, - ".dynarrayStorage"); - gvar->setUnnamedAddr(canBeConst); - llvm::Constant* store = DtoBitCast(gvar, getPtrToType(arrtype)); + void visit(TypeidExp *e) { + IF_LOG Logger::print("TypeidExp::toConstElem: %s @ %s\n", e->toChars(), + e->type->toChars()); - if (bt->ty == Tpointer) - { - // we need to return pointer to the static array. - result = store; - return; - } - - // build a constant dynamic array reference with the .ptr field pointing into store - LLConstant* idxs[2] = { DtoConstUint(0), DtoConstUint(0) }; -#if LDC_LLVM_VER >= 307 - LLConstant* globalstorePtr = llvm::ConstantExpr::getGetElementPtr(isaPointer(store)->getElementType(), store, idxs, true); -#else - LLConstant* globalstorePtr = llvm::ConstantExpr::getGetElementPtr(store, idxs, true); -#endif - - result = DtoConstSlice(DtoConstSize_t(e->elements->dim), globalstorePtr); + Type *t = isType(e->obj); + if (!t) { + visit((Expression *)e); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + TypeInfoDeclaration *tid = getOrCreateTypeInfoDeclaration(t, NULL); + TypeInfoDeclaration_codegen(tid, p); + result = llvm::cast(getIrGlobal(tid)->value); + } - void visit(StructLiteralExp *e) - { - // type can legitimately be null for ClassReferenceExp::value. - IF_LOG Logger::print("StructLiteralExp::toConstElem: %s @ %s\n", - e->toChars(), e->type ? e->type->toChars() : "(null)"); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////////////////////// - if (e->sinit) - { - // Copied from VarExp::toConstElem, need to clean this mess up. - Type* sdecltype = e->sinit->type->toBasetype(); - IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); - assert(sdecltype->ty == Tstruct); - TypeStruct* ts = static_cast(sdecltype); - DtoResolveStruct(ts->sym); + void visit(Expression *e) { + e->error("expression '%s' is not a constant", e->toChars()); + if (!global.gag) + fatal(); - result = getIrAggr(ts->sym)->getDefaultInit(); - } - else - { - // make sure the struct is resolved - DtoResolveStruct(e->sd); - - std::map varInits; - const size_t nexprs = e->elements->dim; - for (size_t i = 0; i < nexprs; i++) - { - if ((*e->elements)[i]) - { - varInits[e->sd->fields[i]] = toConstElem((*e->elements)[i]); - } - } - - result = getIrAggr(e->sd)->createInitializerConstant(varInits); - } - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(ClassReferenceExp *e) - { - IF_LOG Logger::print("ClassReferenceExp::toConstElem: %s @ %s\n", - e->toChars(), e->type->toChars()); - LOG_SCOPE; - - ClassDeclaration* origClass = e->originalClass(); - DtoResolveClass(origClass); - StructLiteralExp *value = e->value; - - if (value->globalVar) - { - IF_LOG Logger::cout() << "Using existing global: " << *value->globalVar << '\n'; - } - else - { - value->globalVar = new llvm::GlobalVariable(p->module, - origClass->type->ctype->isClass()->getMemoryLLType(), - false, llvm::GlobalValue::InternalLinkage, 0, ".classref"); - - std::map varInits; - - // Unfortunately, ClassReferenceExp::getFieldAt is badly broken – it - // places the base class fields _after_ those of the subclass. - { - const size_t nexprs = value->elements->dim; - - std::stack classHierachy; - ClassDeclaration* cur = origClass; - while (cur) - { - classHierachy.push(cur); - cur = cur->baseClass; - } - size_t i = 0; - while (!classHierachy.empty()) - { - cur = classHierachy.top(); - classHierachy.pop(); - for (size_t j = 0; j < cur->fields.dim; ++j) - { - if ((*value->elements)[i]) - { - VarDeclaration* field = cur->fields[j]; - IF_LOG Logger::println("Getting initializer for: %s", field->toChars()); - LOG_SCOPE; - varInits[field] = toConstElem((*value->elements)[i]); - } - ++i; - } - } - assert(i == nexprs); - } - - llvm::Constant* constValue = getIrAggr(origClass)->createInitializerConstant(varInits); - - if (constValue->getType() != value->globalVar->getType()->getContainedType(0)) - { - llvm::GlobalVariable* finalGlobalVar = new llvm::GlobalVariable( - p->module, constValue->getType(), false, - llvm::GlobalValue::InternalLinkage, 0, ".classref"); - value->globalVar->replaceAllUsesWith( - DtoBitCast(finalGlobalVar, value->globalVar->getType())); - value->globalVar->eraseFromParent(); - value->globalVar = finalGlobalVar; - } - value->globalVar->setInitializer(constValue); - } - - result = value->globalVar; - - if (e->type->ty == Tclass) { - ClassDeclaration* targetClass = static_cast(e->type)->sym; - if (InterfaceDeclaration* it = targetClass->isInterfaceDeclaration()) { - assert(it->isBaseOf(origClass, NULL)); - - IrTypeClass* typeclass = origClass->type->ctype->isClass(); - - // find interface impl - size_t i_index = typeclass->getInterfaceIndex(it); - assert(i_index != ~0UL); - - // offset pointer - result = DtoGEPi(result, 0, i_index); - } - } - - assert(e->type->ty == Tclass || e->type->ty == Tenum); - result = DtoBitCast(result, DtoType(e->type)); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(VectorExp *e) - { - IF_LOG Logger::print("VectorExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - TypeVector *tv = static_cast(e->to->toBasetype()); - assert(tv->ty == Tvector); - - // The AST for - // static immutable ubyte16 vec1 = 123; - // differs from - // static immutable ubyte[16] vec1 = 123; - // In the vector case the AST contains an IntegerExp (of type int) and a - // CastExp to type ubyte. In the static array case the AST only contains an - // IntegerExp of type ubyte. Simply call optimize to get rid of the cast. - // FIXME: Check DMD source to understand why two different ASTs are - // constructed. - llvm::Constant *val = toConstElem(e->e1->optimize(WANTvalue)); - - dinteger_t elemCount = - static_cast(tv->basetype)->dim->toInteger(); - result = llvm::ConstantVector::getSplat(elemCount, val); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(TypeidExp *e) - { - IF_LOG Logger::print("TypeidExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); - - Type *t = isType(e->obj); - if (!t) - { - visit((Expression*)e); - return; - } - - TypeInfoDeclaration *tid = getOrCreateTypeInfoDeclaration(t, NULL); - TypeInfoDeclaration_codegen(tid, p); - result = llvm::cast(getIrGlobal(tid)->value); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(Expression *e) - { - e->error("expression '%s' is not a constant", e->toChars()); - if (!global.gag) - fatal(); - - // Do not return null here, as AssocArrayLiteralExp::toElem determines - // whether it can allocate the needed arrays statically by just invoking - // toConstElem on its key/value expressions, and handling the null value - // consequently would require error-prone adaptions in all other code. - result = llvm::UndefValue::get(DtoType(e->type)); - } + // Do not return null here, as AssocArrayLiteralExp::toElem determines + // whether it can allocate the needed arrays statically by just invoking + // toConstElem on its key/value expressions, and handling the null value + // consequently would require error-prone adaptions in all other code. + result = llvm::UndefValue::get(DtoType(e->type)); + } }; -LLConstant *toConstElem(Expression *e, IRState *p) -{ - return ToConstElemVisitor(p).toConstElem(e); +LLConstant *toConstElem(Expression *e, IRState *p) { + return ToConstElemVisitor(p).toConstElem(e); } diff --git a/gen/toir.cpp b/gen/toir.cpp index 242d9c352a..3aeb673d14 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -50,2987 +50,2852 @@ // Needs other includes. #include "ctfe.h" -llvm::cl::opt checkPrintf("check-printf-calls", +llvm::cl::opt checkPrintf( + "check-printf-calls", llvm::cl::desc("Validate printf call format strings against arguments"), llvm::cl::ZeroOrMore); - bool walkPostorder(Expression *e, StoppableVisitor *v); -extern LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init); +extern LLConstant *get_default_initializer(VarDeclaration *vd, + Initializer *init); ////////////////////////////////////////////////////////////////////////////////////////////// -dinteger_t undoStrideMul(Loc& loc, Type* t, dinteger_t offset) -{ - assert(t->ty == Tpointer); - d_uns64 elemSize = t->nextOf()->size(loc); - assert((offset % elemSize) == 0 && - "Expected offset by an integer amount of elements"); +dinteger_t undoStrideMul(Loc &loc, Type *t, dinteger_t offset) { + assert(t->ty == Tpointer); + d_uns64 elemSize = t->nextOf()->size(loc); + assert((offset % elemSize) == 0 && + "Expected offset by an integer amount of elements"); - return offset / elemSize; + return offset / elemSize; } ////////////////////////////////////////////////////////////////////////////////////////////// -static LLValue* write_zeroes(LLValue* mem, unsigned start, unsigned end) -{ - mem = DtoBitCast(mem, getVoidPtrType()); - LLValue* gep = DtoGEPi1(mem, start, ".padding"); - DtoMemSetZero(gep, DtoConstSize_t(end - start)); - return mem; +static LLValue *write_zeroes(LLValue *mem, unsigned start, unsigned end) { + mem = DtoBitCast(mem, getVoidPtrType()); + LLValue *gep = DtoGEPi1(mem, start, ".padding"); + DtoMemSetZero(gep, DtoConstSize_t(end - start)); + return mem; } ////////////////////////////////////////////////////////////////////////////////////////////// -static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd, Expressions *elements) -{ - // ready elements data - assert(elements && "struct literal has null elements"); - const size_t nexprs = elements->dim; - Expression **exprs = reinterpret_cast(elements->data); +static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd, + Expressions *elements) { + // ready elements data + assert(elements && "struct literal has null elements"); + const size_t nexprs = elements->dim; + Expression **exprs = reinterpret_cast(elements->data); - // might be reset to an actual i8* value so only a single bitcast is emitted. - LLValue *voidptr = mem; - unsigned offset = 0; + // might be reset to an actual i8* value so only a single bitcast is emitted. + LLValue *voidptr = mem; + unsigned offset = 0; - // go through fields - const size_t nfields = sd->fields.dim; - for (size_t index = 0; index < nfields; ++index) - { - VarDeclaration *vd = sd->fields[index]; + // go through fields + const size_t nfields = sd->fields.dim; + for (size_t index = 0; index < nfields; ++index) { + VarDeclaration *vd = sd->fields[index]; - // get initializer expression - Expression* expr = (index < nexprs) ? exprs[index] : NULL; - if (!expr) - { - // In case of an union, we can't simply use the default initializer. - // Consider the type union U7727A1 { int i; double d; } and - // the declaration U7727A1 u = { d: 1.225 }; - // The loop will first visit variable i and then d. Since d has an - // explicit initializer, we must use this one. The solution is to - // peek at the next variables. - for (size_t index2 = index+1; index2 < nfields; ++index2) - { - VarDeclaration *vd2 = sd->fields[index2]; - if (vd->offset != vd2->offset) break; - ++index; // skip var - Expression* expr2 = (index2 < nexprs) ? exprs[index2] : NULL; - if (expr2) - { - vd = vd2; - expr = expr2; - break; - } - } - } - - // don't re-initialize unions - if (vd->offset < offset) - { - IF_LOG Logger::println("skipping field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); - continue; - } - - // initialize any padding so struct comparisons work - if (vd->offset != offset) - voidptr = write_zeroes(voidptr, offset, vd->offset); - offset = vd->offset + vd->type->size(); - - IF_LOG Logger::println("initializing field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); - LOG_SCOPE - - // get initializer - DValue* val; - DConstValue cv(vd->type, NULL); // Only used in one branch; value is set beforehand - if (expr) - { - IF_LOG Logger::println("expr %llu = %s", static_cast(index), expr->toChars()); - val = toElem(expr); - } - else if (vd == sd->vthis) { - IF_LOG Logger::println("initializing vthis"); - LOG_SCOPE - val = new DImValue(vd->type, DtoBitCast(DtoNestedContext(loc, sd), DtoType(vd->type))); - } - else - { - if (vd->init && vd->init->isVoidInitializer()) - continue; - IF_LOG Logger::println("using default initializer"); - LOG_SCOPE - cv.c = get_default_initializer(vd, NULL); - val = &cv; - } - - // get a pointer to this field - DVarValue field(vd->type, vd, DtoIndexAggregate(mem, sd, vd)); - - // store the initializer there - DtoAssign(loc, &field, val, TOKconstruct, true); - - if (expr && expr->isLvalue()) - callPostblit(loc, expr, field.getLVal()); - - // Also zero out padding bytes counted as being part of the type in DMD - // but not in LLVM; e.g. real/x86_fp80. - int implicitPadding = - vd->type->size() - gDataLayout->getTypeStoreSize(DtoType(vd->type)); - assert(implicitPadding >= 0); - if (implicitPadding > 0) - { - IF_LOG Logger::println("zeroing %d padding bytes", implicitPadding); - voidptr = write_zeroes(voidptr, offset - implicitPadding, offset); + // get initializer expression + Expression *expr = (index < nexprs) ? exprs[index] : NULL; + if (!expr) { + // In case of an union, we can't simply use the default initializer. + // Consider the type union U7727A1 { int i; double d; } and + // the declaration U7727A1 u = { d: 1.225 }; + // The loop will first visit variable i and then d. Since d has an + // explicit initializer, we must use this one. The solution is to + // peek at the next variables. + for (size_t index2 = index + 1; index2 < nfields; ++index2) { + VarDeclaration *vd2 = sd->fields[index2]; + if (vd->offset != vd2->offset) + break; + ++index; // skip var + Expression *expr2 = (index2 < nexprs) ? exprs[index2] : NULL; + if (expr2) { + vd = vd2; + expr = expr2; + break; } + } } - // initialize trailing padding - if (sd->structsize != offset) - voidptr = write_zeroes(voidptr, offset, sd->structsize); + + // don't re-initialize unions + if (vd->offset < offset) { + IF_LOG Logger::println("skipping field: %s %s (+%u)", vd->type->toChars(), + vd->toChars(), vd->offset); + continue; + } + + // initialize any padding so struct comparisons work + if (vd->offset != offset) + voidptr = write_zeroes(voidptr, offset, vd->offset); + offset = vd->offset + vd->type->size(); + + IF_LOG Logger::println("initializing field: %s %s (+%u)", + vd->type->toChars(), vd->toChars(), vd->offset); + LOG_SCOPE + + // get initializer + DValue *val; + DConstValue cv(vd->type, + NULL); // Only used in one branch; value is set beforehand + if (expr) { + IF_LOG Logger::println("expr %llu = %s", + static_cast(index), + expr->toChars()); + val = toElem(expr); + } else if (vd == sd->vthis) { + IF_LOG Logger::println("initializing vthis"); + LOG_SCOPE + val = new DImValue( + vd->type, DtoBitCast(DtoNestedContext(loc, sd), DtoType(vd->type))); + } else { + if (vd->init && vd->init->isVoidInitializer()) + continue; + IF_LOG Logger::println("using default initializer"); + LOG_SCOPE + cv.c = get_default_initializer(vd, NULL); + val = &cv; + } + + // get a pointer to this field + DVarValue field(vd->type, vd, DtoIndexAggregate(mem, sd, vd)); + + // store the initializer there + DtoAssign(loc, &field, val, TOKconstruct, true); + + if (expr && expr->isLvalue()) + callPostblit(loc, expr, field.getLVal()); + + // Also zero out padding bytes counted as being part of the type in DMD + // but not in LLVM; e.g. real/x86_fp80. + int implicitPadding = + vd->type->size() - gDataLayout->getTypeStoreSize(DtoType(vd->type)); + assert(implicitPadding >= 0); + if (implicitPadding > 0) { + IF_LOG Logger::println("zeroing %d padding bytes", implicitPadding); + voidptr = write_zeroes(voidptr, offset - implicitPadding, offset); + } + } + // initialize trailing padding + if (sd->structsize != offset) + voidptr = write_zeroes(voidptr, offset, sd->structsize); } -namespace -{ -void pushVarDtorCleanup(IRState* p, VarDeclaration* vd) -{ - llvm::BasicBlock *beginBB = llvm::BasicBlock::Create( - p->context(), llvm::Twine("dtor.") + vd->toChars(), p->topfunc()); +namespace { +void pushVarDtorCleanup(IRState *p, VarDeclaration *vd) { + llvm::BasicBlock *beginBB = llvm::BasicBlock::Create( + p->context(), llvm::Twine("dtor.") + vd->toChars(), p->topfunc()); - // TODO: Clean this up with push/pop insertion point methods. - IRScope oldScope = p->scope(); - p->scope() = IRScope(beginBB); - toElemDtor(vd->edtor); - p->func()->scopes->pushCleanup(beginBB, p->scopebb()); - p->scope() = oldScope; + // TODO: Clean this up with push/pop insertion point methods. + IRScope oldScope = p->scope(); + p->scope() = IRScope(beginBB); + toElemDtor(vd->edtor); + p->func()->scopes->pushCleanup(beginBB, p->scopebb()); + p->scope() = oldScope; } } ////////////////////////////////////////////////////////////////////////////////////////////// -// Tries to find the proper lvalue subexpression of an assign/binassign expression. +// Tries to find the proper lvalue subexpression of an assign/binassign +// expression. // Returns null if none is found. -static Expression* findLvalueExp(Expression* e) -{ - class FindLvalueVisitor : public Visitor - { - public: - Expression* result; +static Expression *findLvalueExp(Expression *e) { + class FindLvalueVisitor : public Visitor { + public: + Expression *result; - FindLvalueVisitor() : result(NULL) {} + FindLvalueVisitor() : result(NULL) {} - void visit(Expression* e) LLVM_OVERRIDE{} + void visit(Expression *e) LLVM_OVERRIDE {} - #define FORWARD(TYPE) void visit(TYPE* e) LLVM_OVERRIDE { e->e1->accept(this); } - FORWARD(AssignExp) - FORWARD(BinAssignExp) - FORWARD(CastExp) - #undef FORWARD +#define FORWARD(TYPE) \ + void visit(TYPE *e) LLVM_OVERRIDE { e->e1->accept(this); } + FORWARD(AssignExp) + FORWARD(BinAssignExp) + FORWARD(CastExp) +#undef FORWARD - #define IMPLEMENT(TYPE) void visit(TYPE* e) LLVM_OVERRIDE { result = e; } - IMPLEMENT(VarExp) - IMPLEMENT(CallExp) - IMPLEMENT(PtrExp) - IMPLEMENT(DotVarExp) - IMPLEMENT(IndexExp) - IMPLEMENT(CommaExp) - #undef IMPLEMENT - }; +#define IMPLEMENT(TYPE) \ + void visit(TYPE *e) LLVM_OVERRIDE { result = e; } + IMPLEMENT(VarExp) + IMPLEMENT(CallExp) + IMPLEMENT(PtrExp) + IMPLEMENT(DotVarExp) + IMPLEMENT(IndexExp) + IMPLEMENT(CommaExp) +#undef IMPLEMENT + }; - FindLvalueVisitor v; - e->accept(&v); - return v.result; + FindLvalueVisitor v; + e->accept(&v); + return v.result; } // Evaluates an lvalue expression e and prevents further // evaluations as long as e->cachedLvalue isn't reset to null. -static DValue* toElemAndCacheLvalue(Expression* e) -{ - DValue* value = toElem(e); - e->cachedLvalue = value->getLVal(); - return value; +static DValue *toElemAndCacheLvalue(Expression *e) { + DValue *value = toElem(e); + e->cachedLvalue = value->getLVal(); + return value; } // Evaluates e and, if tryGetLvalue is true, returns the // (casted) nested lvalue if one is found. // Otherwise simply returns the expression's result. -DValue* toElem(Expression* e, bool tryGetLvalue) -{ - if (!tryGetLvalue) - return toElem(e); +DValue *toElem(Expression *e, bool tryGetLvalue) { + if (!tryGetLvalue) + return toElem(e); - Expression* lvalExp = findLvalueExp(e); // may be null - Expression* nestedLvalExp = (lvalExp == e ? NULL : lvalExp); + Expression *lvalExp = findLvalueExp(e); // may be null + Expression *nestedLvalExp = (lvalExp == e ? NULL : lvalExp); - DValue* nestedLval = NULL; - if (nestedLvalExp) - { - IF_LOG Logger::println("Caching l-value of %s => %s", - e->toChars(), nestedLvalExp->toChars()); + DValue *nestedLval = NULL; + if (nestedLvalExp) { + IF_LOG Logger::println("Caching l-value of %s => %s", e->toChars(), + nestedLvalExp->toChars()); - LOG_SCOPE; - nestedLval = toElemAndCacheLvalue(nestedLvalExp); - } + LOG_SCOPE; + nestedLval = toElemAndCacheLvalue(nestedLvalExp); + } - DValue* value = toElem(e); + DValue *value = toElem(e); - if (nestedLvalExp) - nestedLvalExp->cachedLvalue = NULL; + if (nestedLvalExp) + nestedLvalExp->cachedLvalue = NULL; - return !nestedLval ? value : DtoCast(e->loc, nestedLval, e->type); + return !nestedLval ? value : DtoCast(e->loc, nestedLval, e->type); } ////////////////////////////////////////////////////////////////////////////////////////////// -class ToElemVisitor : public Visitor -{ - IRState *p; - bool destructTemporaries; - CleanupCursor initialCleanupScope; - DValue *result; +class ToElemVisitor : public Visitor { + IRState *p; + bool destructTemporaries; + CleanupCursor initialCleanupScope; + DValue *result; + public: - ToElemVisitor(IRState *p_, bool destructTemporaries_) - : p(p_), destructTemporaries(destructTemporaries_), result(NULL) - { - initialCleanupScope = p->func()->scopes->currentCleanupScope(); + ToElemVisitor(IRState *p_, bool destructTemporaries_) + : p(p_), destructTemporaries(destructTemporaries_), result(NULL) { + initialCleanupScope = p->func()->scopes->currentCleanupScope(); + } + + DValue *getResult() { + if (destructTemporaries && + p->func()->scopes->currentCleanupScope() != initialCleanupScope) { + // If the results is an (LLVM) r-value, temporarily store it in an + // alloca slot to avoid running into instruction dominance issues + // if we share the cleanups with another exit path (e.g. unwinding). + if (result && result->getType()->ty != Tvoid && + (result->isIm() || result->isSlice())) { + LLValue *alloca = DtoAllocaDump(result); + result = new DVarValue(result->getType(), alloca); + } + + llvm::BasicBlock *endbb = llvm::BasicBlock::Create( + p->context(), "toElem.success", p->topfunc()); + p->func()->scopes->runCleanups(initialCleanupScope, endbb); + p->func()->scopes->popCleanups(initialCleanupScope); + p->scope() = IRScope(endbb); + + destructTemporaries = false; } - DValue *getResult() { - if (destructTemporaries && - p->func()->scopes->currentCleanupScope() != initialCleanupScope - ) { - // If the results is an (LLVM) r-value, temporarily store it in an - // alloca slot to avoid running into instruction dominance issues - // if we share the cleanups with another exit path (e.g. unwinding). - if (result && result->getType()->ty != Tvoid && - (result->isIm() || result->isSlice()) - ) { - LLValue* alloca = DtoAllocaDump(result); - result = new DVarValue(result->getType(), alloca); - } + return result; + } - llvm::BasicBlock* endbb = llvm::BasicBlock::Create( - p->context(), "toElem.success", p->topfunc()); - p->func()->scopes->runCleanups(initialCleanupScope, endbb); - p->func()->scopes->popCleanups(initialCleanupScope); - p->scope() = IRScope(endbb); + ////////////////////////////////////////////////////////////////////////////////////////// - destructTemporaries = false; + // Import all functions from class Visitor + using Visitor::visit; + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(DeclarationExp *e) { + IF_LOG Logger::print("DeclarationExp::toElem: %s | T=%s\n", e->toChars(), + e->type ? e->type->toChars() : "(null)"); + LOG_SCOPE; + + result = DtoDeclarationExp(e->declaration); + + if (result) { + if (DVarValue *varValue = result->isVar()) { + VarDeclaration *vd = varValue->var; + if (!vd->isDataseg() && vd->edtor && !vd->noscope) { + pushVarDtorCleanup(p, vd); } + } + } + } - return result; + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(VarExp *e) { + IF_LOG Logger::print("VarExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + assert(e->var); + + if (e->cachedLvalue) { + LLValue *V = e->cachedLvalue; + result = new DVarValue(e->type, V); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + result = DtoSymbolAddress(e->loc, e->type, e->var); + } - // Import all functions from class Visitor - using Visitor::visit; + ////////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////// + void visit(IntegerExp *e) { + IF_LOG Logger::print("IntegerExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + LLConstant *c = toConstElem(e, p); + result = new DConstValue(e->type, c); + } - void visit(DeclarationExp *e) - { - IF_LOG Logger::print("DeclarationExp::toElem: %s | T=%s\n", e->toChars(), - e->type ? e->type->toChars() : "(null)"); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////////////////////// - result = DtoDeclarationExp(e->declaration); + void visit(RealExp *e) { + IF_LOG Logger::print("RealExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + LLConstant *c = toConstElem(e, p); + result = new DConstValue(e->type, c); + } - if (result) - { - if (DVarValue* varValue = result->isVar()) - { - VarDeclaration* vd = varValue->var; - if (!vd->isDataseg() && vd->edtor && !vd->noscope) - { - pushVarDtorCleanup(p, vd); - } - } - } + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(NullExp *e) { + IF_LOG Logger::print("NullExp::toElem(type=%s): %s\n", e->type->toChars(), + e->toChars()); + LOG_SCOPE; + LLConstant *c = toConstElem(e, p); + result = new DNullValue(e->type, c); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(ComplexExp *e) { + IF_LOG Logger::print("ComplexExp::toElem(): %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + LLConstant *c = toConstElem(e, p); + LLValue *res; + + if (c->isNullValue()) { + switch (e->type->toBasetype()->ty) { + default: + llvm_unreachable("Unexpected complex floating point type"); + case Tcomplex32: + c = DtoConstFP(Type::tfloat32, ldouble(0)); + break; + case Tcomplex64: + c = DtoConstFP(Type::tfloat64, ldouble(0)); + break; + case Tcomplex80: + c = DtoConstFP(Type::tfloat80, ldouble(0)); + break; + } + res = DtoAggrPair(DtoType(e->type), c, c); + } else { + res = DtoAggrPair(DtoType(e->type), c->getOperand(0), c->getOperand(1)); } - ////////////////////////////////////////////////////////////////////////////////////////// + result = new DImValue(e->type, res); + } - void visit(VarExp *e) - { - IF_LOG Logger::print("VarExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////////////////////// - assert(e->var); + void visit(StringExp *e) { + IF_LOG Logger::print("StringExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - if (e->cachedLvalue) - { - LLValue* V = e->cachedLvalue; - result = new DVarValue(e->type, V); - return; - } + Type *dtype = e->type->toBasetype(); + Type *cty = dtype->nextOf()->toBasetype(); - result = DtoSymbolAddress(e->loc, e->type, e->var); + LLType *ct = DtoMemType(cty); + LLArrayType *at = LLArrayType::get(ct, e->len + 1); + + llvm::StringMap *stringLiteralCache = 0; + LLConstant *_init; + switch (cty->size()) { + default: + llvm_unreachable("Unknown char type"); + case 1: + _init = + toConstantArray(ct, at, static_cast(e->string), e->len); + stringLiteralCache = &(gIR->stringLiteral1ByteCache); + break; + case 2: + _init = + toConstantArray(ct, at, static_cast(e->string), e->len); + stringLiteralCache = &(gIR->stringLiteral2ByteCache); + break; + case 4: + _init = + toConstantArray(ct, at, static_cast(e->string), e->len); + stringLiteralCache = &(gIR->stringLiteral4ByteCache); + break; } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(IntegerExp *e) - { - IF_LOG Logger::print("IntegerExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - LLConstant* c = toConstElem(e, p); - result = new DConstValue(e->type, c); + llvm::StringRef key(e->toChars()); + llvm::GlobalVariable *gvar = + (stringLiteralCache->find(key) == stringLiteralCache->end()) + ? 0 + : (*stringLiteralCache)[key]; + if (gvar == 0) { + llvm::GlobalValue::LinkageTypes _linkage = + llvm::GlobalValue::PrivateLinkage; + IF_LOG { + Logger::cout() << "type: " << *at << '\n'; + Logger::cout() << "init: " << *_init << '\n'; + } + gvar = new llvm::GlobalVariable(gIR->module, at, true, _linkage, _init, + ".str"); + gvar->setUnnamedAddr(true); + (*stringLiteralCache)[key] = gvar; } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(RealExp *e) - { - IF_LOG Logger::print("RealExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - LLConstant* c = toConstElem(e, p); - result = new DConstValue(e->type, c); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(NullExp *e) - { - IF_LOG Logger::print("NullExp::toElem(type=%s): %s\n", e->type->toChars(), e->toChars()); - LOG_SCOPE; - LLConstant* c = toConstElem(e, p); - result = new DNullValue(e->type, c); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(ComplexExp *e) - { - IF_LOG Logger::print("ComplexExp::toElem(): %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - LLConstant* c = toConstElem(e, p); - LLValue* res; - - if (c->isNullValue()) { - switch (e->type->toBasetype()->ty) { - default: llvm_unreachable("Unexpected complex floating point type"); - case Tcomplex32: c = DtoConstFP(Type::tfloat32, ldouble(0)); break; - case Tcomplex64: c = DtoConstFP(Type::tfloat64, ldouble(0)); break; - case Tcomplex80: c = DtoConstFP(Type::tfloat80, ldouble(0)); break; - } - res = DtoAggrPair(DtoType(e->type), c, c); - } - else { - res = DtoAggrPair(DtoType(e->type), c->getOperand(0), c->getOperand(1)); - } - - result = new DImValue(e->type, res); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(StringExp *e) - { - IF_LOG Logger::print("StringExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - Type* dtype = e->type->toBasetype(); - Type* cty = dtype->nextOf()->toBasetype(); - - LLType* ct = DtoMemType(cty); - LLArrayType* at = LLArrayType::get(ct, e->len+1); - - llvm::StringMap* stringLiteralCache = 0; - LLConstant* _init; - switch (cty->size()) - { - default: - llvm_unreachable("Unknown char type"); - case 1: - _init = toConstantArray(ct, at, static_cast(e->string), e->len); - stringLiteralCache = &(gIR->stringLiteral1ByteCache); - break; - case 2: - _init = toConstantArray(ct, at, static_cast(e->string), e->len); - stringLiteralCache = &(gIR->stringLiteral2ByteCache); - break; - case 4: - _init = toConstantArray(ct, at, static_cast(e->string), e->len); - stringLiteralCache = &(gIR->stringLiteral4ByteCache); - break; - } - - llvm::StringRef key(e->toChars()); - llvm::GlobalVariable* gvar = (stringLiteralCache->find(key) == - stringLiteralCache->end()) - ? 0 : (*stringLiteralCache)[key]; - if (gvar == 0) - { - llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::PrivateLinkage; - IF_LOG { - Logger::cout() << "type: " << *at << '\n'; - Logger::cout() << "init: " << *_init << '\n'; - } - gvar = new llvm::GlobalVariable(gIR->module, at, true, _linkage, _init, ".str"); - gvar->setUnnamedAddr(true); - (*stringLiteralCache)[key] = gvar; - } - - llvm::ConstantInt* zero = LLConstantInt::get(LLType::getInt32Ty(gIR->context()), 0, false); - LLConstant* idxs[2] = { zero, zero }; + llvm::ConstantInt *zero = + LLConstantInt::get(LLType::getInt32Ty(gIR->context()), 0, false); + LLConstant *idxs[2] = {zero, zero}; #if LDC_LLVM_VER >= 307 - LLConstant* arrptr = llvm::ConstantExpr::getGetElementPtr(isaPointer(gvar)->getElementType(), gvar, idxs, true); + LLConstant *arrptr = llvm::ConstantExpr::getGetElementPtr( + isaPointer(gvar)->getElementType(), gvar, idxs, true); #else - LLConstant* arrptr = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true); + LLConstant *arrptr = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true); #endif - if (dtype->ty == Tarray) { - LLConstant* clen = LLConstantInt::get(DtoSize_t(), e->len, false); - result = new DImValue(e->type, DtoConstSlice(clen, arrptr, dtype)); - } - else if (dtype->ty == Tsarray) { - LLType* dstType = getPtrToType(LLArrayType::get(ct, e->len)); - LLValue* emem = (gvar->getType() == dstType) ? gvar : DtoBitCast(gvar, dstType); - result = new DVarValue(e->type, emem); - } - else if (dtype->ty == Tpointer) { - result = new DImValue(e->type, arrptr); - } else { - llvm_unreachable("Unknown type for StringExp."); - } + if (dtype->ty == Tarray) { + LLConstant *clen = LLConstantInt::get(DtoSize_t(), e->len, false); + result = new DImValue(e->type, DtoConstSlice(clen, arrptr, dtype)); + } else if (dtype->ty == Tsarray) { + LLType *dstType = getPtrToType(LLArrayType::get(ct, e->len)); + LLValue *emem = + (gvar->getType() == dstType) ? gvar : DtoBitCast(gvar, dstType); + result = new DVarValue(e->type, emem); + } else if (dtype->ty == Tpointer) { + result = new DImValue(e->type, arrptr); + } else { + llvm_unreachable("Unknown type for StringExp."); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(AssignExp *e) { + IF_LOG Logger::print("AssignExp::toElem: %s | (%s)(%s = %s)\n", + e->toChars(), e->type->toChars(), + e->e1->type->toChars(), + e->e2->type ? e->e2->type->toChars() : 0); + LOG_SCOPE; + + if (e->e1->op == TOKarraylength) { + Logger::println("performing array.length assignment"); + ArrayLengthExp *ale = static_cast(e->e1); + DValue *arr = toElem(ale->e1); + DVarValue arrval(ale->e1->type, arr->getLVal()); + DValue *newlen = toElem(e->e2); + DSliceValue *slice = DtoResizeDynArray(e->loc, arrval.getType(), &arrval, + newlen->getRVal()); + DtoAssign(e->loc, &arrval, slice); + result = newlen; + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + // Initialization of ref variable? + // Can't just override ConstructExp::toElem because not all TOKconstruct + // operations are actually instances of ConstructExp... Long live the DMD + // coding style! + if (e->op == TOKconstruct && e->e1->op == TOKvar && !(e->ismemset & 2)) { + Declaration *d = static_cast(e->e1)->var; + if (d->storage_class & (STCref | STCout)) { + Logger::println("performing ref variable initialization"); + // Note that the variable value is accessed directly (instead + // of via getLVal(), which would perform a load from the + // uninitialized location), and that rhs is stored as an l-value! + DVarValue *lhs = toElem(e->e1)->isVar(); + assert(lhs); + result = toElem(e->e2); - void visit(AssignExp *e) + // We shouldn't really need makeLValue() here, but the 2.063 + // frontend generates ref variables initialized from function + // calls. + DtoStore(makeLValue(e->loc, result), lhs->getRefStorage()); + + return; + } + } + + if (e->e1->op == TOKslice) { + // Check if this is an initialization of a static array with an array + // literal that the frontend has foolishly rewritten into an + // assignment of a dynamic array literal to a slice. + Logger::println("performing static array literal assignment"); + SliceExp *const se = static_cast(e->e1); + Type *const t2 = e->e2->type->toBasetype(); + Type *const ta = se->e1->type->toBasetype(); + + if (se->lwr == NULL && ta->ty == Tsarray && + e->e2->op == TOKarrayliteral && + e->op == TOKconstruct && // DMD Bugzilla 11238: avoid aliasing issue + t2->nextOf()->mutableOf()->implicitConvTo(ta->nextOf())) { + ArrayLiteralExp *const ale = static_cast(e->e2); + initializeArrayLiteral(p, ale, toElem(se->e1)->getLVal()); + result = toElem(e->e1); + return; + } + } + + DValue *l = toElem(e->e1, true); + + // NRVO for object field initialization in constructor + if (l->isVar() && e->op == TOKconstruct && e->e2->op == TOKcall) { + CallExp *ce = static_cast(e->e2); + if (DtoIsReturnInArg(ce)) { + DValue *fnval = toElem(ce->e1); + LLValue *lval = l->getLVal(); + result = DtoCallFunction(ce->loc, ce->type, fnval, ce->arguments, lval); + return; + } + } + + DValue *r = toElem(e->e2); + + if (e->e1->type->toBasetype()->ty == Tstruct && e->e2->op == TOKint64) { + Logger::println("performing aggregate zero initialization"); + assert(e->e2->toInteger() == 0); + DtoAggrZeroInit(l->getLVal()); + TypeStruct *ts = static_cast(e->e1->type); + if (ts->sym->isNested() && ts->sym->vthis) + DtoResolveNestedContext(e->loc, ts->sym, l->getLVal()); + // Return value should be irrelevant. + result = r; + return; + } + + // This matches the logic in AssignExp::semantic. + // TODO: Should be cached in the frontend to avoid issues with the code + // getting out of sync? + bool lvalueElem = false; + if ((e->e2->op == TOKslice && + static_cast(e->e2)->e1->isLvalue()) || + (e->e2->op == TOKcast && + static_cast(e->e2)->e1->isLvalue()) || + (e->e2->op != TOKslice && e->e2->isLvalue())) { + lvalueElem = true; + } + + Logger::println("performing normal assignment (rhs has lvalue elems = %d)", + lvalueElem); + DtoAssign(e->loc, l, r, e->op, !lvalueElem); + + result = l; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template + static DValue *binAssign(BinAssignExp *e) { + Loc loc = e->loc; + + // find the lhs' lvalue expression + Expression *lvalExp = findLvalueExp(e->e1); + if (!lvalExp) { + e->error("expression %s does not mask any l-value", e->e1->toChars()); + fatal(); + } + + // pre-evaluate and cache the lvalue subexpression + DValue *lval = NULL; { - IF_LOG Logger::print("AssignExp::toElem: %s | (%s)(%s = %s)\n", - e->toChars(), - e->type->toChars(), - e->e1->type->toChars(), - e->e2->type ? e->e2->type->toChars() : 0); - LOG_SCOPE; + IF_LOG Logger::println("Caching l-value of %s => %s", e->toChars(), + lvalExp->toChars()); - if (e->e1->op == TOKarraylength) - { - Logger::println("performing array.length assignment"); - ArrayLengthExp *ale = static_cast(e->e1); - DValue* arr = toElem(ale->e1); - DVarValue arrval(ale->e1->type, arr->getLVal()); - DValue* newlen = toElem(e->e2); - DSliceValue* slice = DtoResizeDynArray(e->loc, arrval.getType(), &arrval, newlen->getRVal()); - DtoAssign(e->loc, &arrval, slice); - result = newlen; - return; - } - - // Initialization of ref variable? - // Can't just override ConstructExp::toElem because not all TOKconstruct - // operations are actually instances of ConstructExp... Long live the DMD - // coding style! - if (e->op == TOKconstruct && e->e1->op == TOKvar && !(e->ismemset & 2)) - { - Declaration* d = static_cast(e->e1)->var; - if (d->storage_class & (STCref | STCout)) - { - Logger::println("performing ref variable initialization"); - // Note that the variable value is accessed directly (instead - // of via getLVal(), which would perform a load from the - // uninitialized location), and that rhs is stored as an l-value! - DVarValue* lhs = toElem(e->e1)->isVar(); - assert(lhs); - result = toElem(e->e2); - - // We shouldn't really need makeLValue() here, but the 2.063 - // frontend generates ref variables initialized from function - // calls. - DtoStore(makeLValue(e->loc, result), lhs->getRefStorage()); - - return; - } - } - - if (e->e1->op == TOKslice) - { - // Check if this is an initialization of a static array with an array - // literal that the frontend has foolishly rewritten into an - // assignment of a dynamic array literal to a slice. - Logger::println("performing static array literal assignment"); - SliceExp * const se = static_cast(e->e1); - Type * const t2 = e->e2->type->toBasetype(); - Type * const ta = se->e1->type->toBasetype(); - - if (se->lwr == NULL && ta->ty == Tsarray && - e->e2->op == TOKarrayliteral && - e->op == TOKconstruct && // DMD Bugzilla 11238: avoid aliasing issue - t2->nextOf()->mutableOf()->implicitConvTo(ta->nextOf())) - { - ArrayLiteralExp * const ale = static_cast(e->e2); - initializeArrayLiteral(p, ale, toElem(se->e1)->getLVal()); - result = toElem(e->e1); - return; - } - } - - DValue* l = toElem(e->e1, true); - - // NRVO for object field initialization in constructor - if (l->isVar() && e->op == TOKconstruct && e->e2->op == TOKcall) - { - CallExp *ce = static_cast(e->e2); - if (DtoIsReturnInArg(ce)) - { - DValue* fnval = toElem(ce->e1); - LLValue *lval = l->getLVal(); - result = DtoCallFunction(ce->loc, ce->type, fnval, ce->arguments, lval); - return; - } - } - - DValue* r = toElem(e->e2); - - if (e->e1->type->toBasetype()->ty == Tstruct && e->e2->op == TOKint64) - { - Logger::println("performing aggregate zero initialization"); - assert(e->e2->toInteger() == 0); - DtoAggrZeroInit(l->getLVal()); - TypeStruct *ts = static_cast(e->e1->type); - if (ts->sym->isNested() && ts->sym->vthis) - DtoResolveNestedContext(e->loc, ts->sym, l->getLVal()); - // Return value should be irrelevant. - result = r; - return; - } - - // This matches the logic in AssignExp::semantic. - // TODO: Should be cached in the frontend to avoid issues with the code - // getting out of sync? - bool lvalueElem = false; - if ((e->e2->op == TOKslice && static_cast(e->e2)->e1->isLvalue()) || - (e->e2->op == TOKcast && static_cast(e->e2)->e1->isLvalue()) || - (e->e2->op != TOKslice && e->e2->isLvalue())) - { - lvalueElem = true; - } - - Logger::println("performing normal assignment (rhs has lvalue elems = %d)", lvalueElem); - DtoAssign(e->loc, l, r, e->op, !lvalueElem); - - result = l; + LOG_SCOPE; + lval = toElemAndCacheLvalue(lvalExp); } - ////////////////////////////////////////////////////////////////////////////////////////// + // evaluate the underlying binary expression + Expression *lhsForBinExp = (useLvalForBinExpLhs ? lvalExp : e->e1); + BinExp binExp(loc, lhsForBinExp, e->e2); + binExp.type = lhsForBinExp->type; + DValue *result = toElem(&binExp); - template - static DValue* binAssign(BinAssignExp* e) - { - Loc loc = e->loc; + lvalExp->cachedLvalue = NULL; - // find the lhs' lvalue expression - Expression* lvalExp = findLvalueExp(e->e1); - if (!lvalExp) - { - e->error("expression %s does not mask any l-value", e->e1->toChars()); - fatal(); - } + // assign the (casted) result to lval + DValue *assignedResult = DtoCast(loc, result, lval->type); + DtoAssign(loc, lval, assignedResult); - // pre-evaluate and cache the lvalue subexpression - DValue* lval = NULL; - { - IF_LOG Logger::println("Caching l-value of %s => %s", - e->toChars(), lvalExp->toChars()); + // return the (casted) result + return e->type == assignedResult->type ? assignedResult + : DtoCast(loc, result, e->type); + } - LOG_SCOPE; - lval = toElemAndCacheLvalue(lvalExp); - } +#define BIN_ASSIGN(Op, useLvalForBinExpLhs) \ + void visit(Op##AssignExp *e) { \ + IF_LOG Logger::print(#Op "AssignExp::toElem: %s @ %s\n", e->toChars(), \ + e->type->toChars()); \ + LOG_SCOPE; \ + result = binAssign(e); \ + } - // evaluate the underlying binary expression - Expression* lhsForBinExp = (useLvalForBinExpLhs ? lvalExp : e->e1); - BinExp binExp(loc, lhsForBinExp, e->e2); - binExp.type = lhsForBinExp->type; - DValue* result = toElem(&binExp); - - lvalExp->cachedLvalue = NULL; - - // assign the (casted) result to lval - DValue* assignedResult = DtoCast(loc, result, lval->type); - DtoAssign(loc, lval, assignedResult); - - // return the (casted) result - return e->type == assignedResult->type - ? assignedResult - : DtoCast(loc, result, e->type); - } - -#define BIN_ASSIGN(Op, useLvalForBinExpLhs) \ - void visit(Op##AssignExp *e) \ - { \ - IF_LOG Logger::print(#Op"AssignExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); \ - LOG_SCOPE; \ - result = binAssign(e); \ - } - - BIN_ASSIGN(Add, false) - BIN_ASSIGN(Min, false) - BIN_ASSIGN(Mul, false) - BIN_ASSIGN(Div, false) - BIN_ASSIGN(Mod, false) - BIN_ASSIGN(And, false) - BIN_ASSIGN(Or, false) - BIN_ASSIGN(Xor, false) - BIN_ASSIGN(Shl, true) - BIN_ASSIGN(Shr, true) - BIN_ASSIGN(Ushr, true) + BIN_ASSIGN(Add, false) + BIN_ASSIGN(Min, false) + BIN_ASSIGN(Mul, false) + BIN_ASSIGN(Div, false) + BIN_ASSIGN(Mod, false) + BIN_ASSIGN(And, false) + BIN_ASSIGN(Or, false) + BIN_ASSIGN(Xor, false) + BIN_ASSIGN(Shl, true) + BIN_ASSIGN(Shr, true) + BIN_ASSIGN(Ushr, true) #undef BIN_ASSIGN - ////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// - void errorOnIllegalArrayOp(Expression* base, Expression* e1, Expression* e2) - { - Type* t1 = e1->type->toBasetype(); - Type* t2 = e2->type->toBasetype(); + void errorOnIllegalArrayOp(Expression *base, Expression *e1, Expression *e2) { + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); - // valid array ops would have been transformed by optimize - if ((t1->ty == Tarray || t1->ty == Tsarray) && - (t2->ty == Tarray || t2->ty == Tsarray) - ) - { - base->error("Array operation %s not recognized", base->toChars()); - fatal(); - } + // valid array ops would have been transformed by optimize + if ((t1->ty == Tarray || t1->ty == Tsarray) && + (t2->ty == Tarray || t2->ty == Tsarray)) { + base->error("Array operation %s not recognized", base->toChars()); + fatal(); + } + } + + /// Tries to remove a MulExp by a constant value of baseSize from e. Returns + /// NULL if not possible. + Expression *extractNoStrideInc(Expression *e, d_uns64 baseSize, + bool &negate) { + MulExp *mul; + while (true) { + if (e->op == TOKneg) { + negate = !negate; + e = static_cast(e)->e1; + continue; + } + + if (e->op == TOKmul) { + mul = static_cast(e); + break; + } + + return NULL; } - /// Tries to remove a MulExp by a constant value of baseSize from e. Returns - /// NULL if not possible. - Expression* extractNoStrideInc(Expression* e, d_uns64 baseSize, bool& negate) - { - MulExp* mul; - while (true) - { - if (e->op == TOKneg) - { - negate = !negate; - e = static_cast(e)->e1; - continue; - } + if (!mul->e2->isConst()) + return NULL; + dinteger_t stride = mul->e2->toInteger(); - if (e->op == TOKmul) - { - mul = static_cast(e); - break; - } + if (stride != baseSize) + return NULL; - return NULL; - } + return mul->e1; + } - if (!mul->e2->isConst()) return NULL; - dinteger_t stride = mul->e2->toInteger(); + DValue *emitPointerOffset(IRState *p, Loc loc, DValue *base, + Expression *offset, bool negateOffset, + Type *resultType) { + // The operand emitted by the frontend is in units of bytes, and not + // pointer elements. We try to undo this before resorting to + // temporarily bitcasting the pointer to i8. - if (stride != baseSize) return NULL; - - return mul->e1; + llvm::Value *noStrideInc = NULL; + if (offset->isConst()) { + dinteger_t byteOffset = offset->toInteger(); + if (byteOffset == 0) { + Logger::println("offset is zero"); + return base; + } + noStrideInc = DtoConstSize_t(undoStrideMul(loc, base->type, byteOffset)); + } else if (Expression *inc = extractNoStrideInc( + offset, base->type->nextOf()->size(loc), negateOffset)) { + noStrideInc = toElem(inc)->getRVal(); } - DValue* emitPointerOffset(IRState* p, Loc loc, DValue* base, - Expression* offset, bool negateOffset, Type* resultType) - { - // The operand emitted by the frontend is in units of bytes, and not - // pointer elements. We try to undo this before resorting to - // temporarily bitcasting the pointer to i8. - - llvm::Value* noStrideInc = NULL; - if (offset->isConst()) - { - dinteger_t byteOffset = offset->toInteger(); - if (byteOffset == 0) - { - Logger::println("offset is zero"); - return base; - } - noStrideInc = DtoConstSize_t(undoStrideMul(loc, base->type, byteOffset)); - } - else if (Expression* inc = extractNoStrideInc(offset, - base->type->nextOf()->size(loc), negateOffset)) - { - noStrideInc = toElem(inc)->getRVal(); - } - - if (noStrideInc) - { - if (negateOffset) noStrideInc = p->ir->CreateNeg(noStrideInc); - return new DImValue(base->type, - DtoGEP1(base->getRVal(), noStrideInc, "", p->scopebb())); - } - - // This might not actually be generated by the frontend, just to be - // safe. - llvm::Value* inc = toElem(offset)->getRVal(); - if (negateOffset) inc = p->ir->CreateNeg(inc); - llvm::Value* bytePtr = DtoBitCast(base->getRVal(), getVoidPtrType()); - DValue* result = new DImValue(Type::tvoidptr, DtoGEP1(bytePtr, inc)); - return DtoCast(loc, result, resultType); + if (noStrideInc) { + if (negateOffset) + noStrideInc = p->ir->CreateNeg(noStrideInc); + return new DImValue( + base->type, DtoGEP1(base->getRVal(), noStrideInc, "", p->scopebb())); } - void visit(AddExp *e) - { - IF_LOG Logger::print("AddExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + // This might not actually be generated by the frontend, just to be + // safe. + llvm::Value *inc = toElem(offset)->getRVal(); + if (negateOffset) + inc = p->ir->CreateNeg(inc); + llvm::Value *bytePtr = DtoBitCast(base->getRVal(), getVoidPtrType()); + DValue *result = new DImValue(Type::tvoidptr, DtoGEP1(bytePtr, inc)); + return DtoCast(loc, result, resultType); + } - DValue* l = toElem(e->e1); + void visit(AddExp *e) { + IF_LOG Logger::print("AddExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - Type* t = e->type->toBasetype(); - Type* e1type = e->e1->type->toBasetype(); - Type* e2type = e->e2->type->toBasetype(); + DValue *l = toElem(e->e1); - errorOnIllegalArrayOp(e, e->e1, e->e2); + Type *t = e->type->toBasetype(); + Type *e1type = e->e1->type->toBasetype(); + Type *e2type = e->e2->type->toBasetype(); - if (e1type != e2type && e1type->ty == Tpointer && e2type->isintegral()) - { - Logger::println("Adding integer to pointer"); - result = emitPointerOffset(p, e->loc, l, e->e2, false, e->type); - } - else if (t->iscomplex()) { - result = DtoComplexAdd(e->loc, e->type, l, toElem(e->e2)); - } - else { - result = DtoBinAdd(l, toElem(e->e2)); - } + errorOnIllegalArrayOp(e, e->e1, e->e2); + + if (e1type != e2type && e1type->ty == Tpointer && e2type->isintegral()) { + Logger::println("Adding integer to pointer"); + result = emitPointerOffset(p, e->loc, l, e->e2, false, e->type); + } else if (t->iscomplex()) { + result = DtoComplexAdd(e->loc, e->type, l, toElem(e->e2)); + } else { + result = DtoBinAdd(l, toElem(e->e2)); + } + } + + void visit(MinExp *e) { + IF_LOG Logger::print("MinExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *l = toElem(e->e1); + + Type *t = e->type->toBasetype(); + Type *t1 = e->e1->type->toBasetype(); + Type *t2 = e->e2->type->toBasetype(); + + errorOnIllegalArrayOp(e, e->e1, e->e2); + + if (t1->ty == Tpointer && t2->ty == Tpointer) { + LLValue *lv = l->getRVal(); + LLValue *rv = toElem(e->e2)->getRVal(); + IF_LOG Logger::cout() << "lv: " << *lv << " rv: " << *rv << '\n'; + lv = p->ir->CreatePtrToInt(lv, DtoSize_t()); + rv = p->ir->CreatePtrToInt(rv, DtoSize_t()); + LLValue *diff = p->ir->CreateSub(lv, rv); + if (diff->getType() != DtoType(e->type)) + diff = p->ir->CreateIntToPtr(diff, DtoType(e->type)); + result = new DImValue(e->type, diff); + } else if (t1->ty == Tpointer && t2->isintegral()) { + Logger::println("Subtracting integer from pointer"); + result = emitPointerOffset(p, e->loc, l, e->e2, true, e->type); + } else if (t->iscomplex()) { + result = DtoComplexSub(e->loc, e->type, l, toElem(e->e2)); + } else { + result = DtoBinSub(l, toElem(e->e2)); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(MulExp *e) { + IF_LOG Logger::print("MulExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *l = toElem(e->e1); + DValue *r = toElem(e->e2); + + errorOnIllegalArrayOp(e, e->e1, e->e2); + + if (e->type->iscomplex()) + result = DtoComplexMul(e->loc, e->type, l, r); + else + result = DtoBinMul(e->type, l, r); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(DivExp *e) { + IF_LOG Logger::print("DivExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *l = toElem(e->e1); + DValue *r = toElem(e->e2); + + errorOnIllegalArrayOp(e, e->e1, e->e2); + + if (e->type->iscomplex()) + result = DtoComplexDiv(e->loc, e->type, l, r); + else + result = DtoBinDiv(e->type, l, r); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(ModExp *e) { + IF_LOG Logger::print("ModExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *l = toElem(e->e1); + DValue *r = toElem(e->e2); + + errorOnIllegalArrayOp(e, e->e1, e->e2); + + if (e->type->iscomplex()) + result = DtoComplexRem(e->loc, e->type, l, r); + else + result = DtoBinRem(e->type, l, r); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(CallExp *e) { + IF_LOG Logger::print("CallExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + if (e->cachedLvalue) { + LLValue *V = e->cachedLvalue; + result = new DVarValue(e->type, V); + return; } - void visit(MinExp *e) - { - IF_LOG Logger::print("MinExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - DValue* l = toElem(e->e1); - - Type* t = e->type->toBasetype(); - Type* t1 = e->e1->type->toBasetype(); - Type* t2 = e->e2->type->toBasetype(); - - errorOnIllegalArrayOp(e, e->e1, e->e2); - - if (t1->ty == Tpointer && t2->ty == Tpointer) { - LLValue* lv = l->getRVal(); - LLValue* rv = toElem(e->e2)->getRVal(); - IF_LOG Logger::cout() << "lv: " << *lv << " rv: " << *rv << '\n'; - lv = p->ir->CreatePtrToInt(lv, DtoSize_t()); - rv = p->ir->CreatePtrToInt(rv, DtoSize_t()); - LLValue* diff = p->ir->CreateSub(lv,rv); - if (diff->getType() != DtoType(e->type)) - diff = p->ir->CreateIntToPtr(diff, DtoType(e->type)); - result = new DImValue(e->type, diff); - } - else if (t1->ty == Tpointer && t2->isintegral()) - { - Logger::println("Subtracting integer from pointer"); - result = emitPointerOffset(p, e->loc, l, e->e2, true, e->type); - } - else if (t->iscomplex()) { - result = DtoComplexSub(e->loc, e->type, l, toElem(e->e2)); - } - else { - result = DtoBinSub(l, toElem(e->e2)); + // handle magic inline asm + if (e->e1->op == TOKvar) { + VarExp *ve = static_cast(e->e1); + if (FuncDeclaration *fd = ve->var->isFuncDeclaration()) { + if (fd->llvmInternal == LLVMinline_asm) { + result = DtoInlineAsmExpr(e->loc, fd, e->arguments); + return; } + } } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(MulExp *e) - { - IF_LOG Logger::print("MulExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - DValue* l = toElem(e->e1); - DValue* r = toElem(e->e2); - - errorOnIllegalArrayOp(e, e->e1, e->e2); - - if (e->type->iscomplex()) - result = DtoComplexMul(e->loc, e->type, l, r); - else - result = DtoBinMul(e->type, l, r); + // Check if we are about to construct a just declared temporary. DMD + // unfortunately rewrites this as + // MyStruct(myArgs) => (MyStruct tmp; tmp).this(myArgs), + // which would lead us to invoke the dtor even if the ctor throws. To + // work around this, we hold on to the cleanup and push it only after + // making the function call. + // + // The correct fix for this (DMD issue 13095) would have been to adapt + // the AST, but we are stuck with this as DMD also patched over it with + // a similar hack. + VarDeclaration *delayedDtorVar = NULL; + Expression *delayedDtorExp = NULL; + if (e->f && e->f->isCtorDeclaration() && e->e1->op == TOKdotvar) { + DotVarExp *dve = static_cast(e->e1); + if (dve->e1->op == TOKcomma) { + CommaExp *ce = static_cast(dve->e1); + if (ce->e1->op == TOKdeclaration && ce->e2->op == TOKvar) { + VarExp *ve = static_cast(ce->e2); + if (VarDeclaration *vd = ve->var->isVarDeclaration()) { + if (vd->edtor && !vd->noscope) { + Logger::println("Delaying edtor"); + delayedDtorVar = vd; + delayedDtorExp = vd->edtor; + vd->edtor = NULL; + } + } + } + } } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(DivExp *e) - { - IF_LOG Logger::print("DivExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - DValue* l = toElem(e->e1); - DValue* r = toElem(e->e2); - - errorOnIllegalArrayOp(e, e->e1, e->e2); - - if (e->type->iscomplex()) - result = DtoComplexDiv(e->loc, e->type, l, r); - else - result = DtoBinDiv(e->type, l, r); + // get the callee value + DValue *fnval; + if (e->directcall) { + // TODO: Do this as an extra parameter to DotVarExp implementation. + assert(e->e1->op == TOKdotvar); + DotVarExp *dve = static_cast(e->e1); + FuncDeclaration *fdecl = dve->var->isFuncDeclaration(); + assert(fdecl); + DtoResolveFunction(fdecl); + fnval = new DFuncValue(fdecl, getIrFunc(fdecl)->func, + toElem(dve->e1)->getRVal()); + } else { + fnval = toElem(e->e1); } - ////////////////////////////////////////////////////////////////////////////////////////// + // get func value if any + DFuncValue *dfnval = fnval->isFunc(); - void visit(ModExp *e) - { - IF_LOG Logger::print("ModExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + // handle magic intrinsics (mapping to instructions) + if (dfnval && dfnval->func) { + FuncDeclaration *fndecl = dfnval->func; - DValue* l = toElem(e->e1); - DValue* r = toElem(e->e2); + // as requested by bearophile, see if it's a C printf call and that it's + // valid. + if (global.params.warnings && checkPrintf) { + if (fndecl->linkage == LINKc && + strcmp(fndecl->ident->string, "printf") == 0) { + warnInvalidPrintfCall(e->loc, (*e->arguments)[0], e->arguments->dim); + } + } - errorOnIllegalArrayOp(e, e->e1, e->e2); - - if (e->type->iscomplex()) - result = DtoComplexRem(e->loc, e->type, l, r); - else - result = DtoBinRem(e->type, l, r); + if (DtoLowerMagicIntrinsic(p, fndecl, e, result)) + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + result = DtoCallFunction(e->loc, e->type, fnval, e->arguments); - void visit(CallExp *e) - { - IF_LOG Logger::print("CallExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + if (delayedDtorVar) { + delayedDtorVar->edtor = delayedDtorExp; + pushVarDtorCleanup(p, delayedDtorVar); + } + } - if (e->cachedLvalue) - { - LLValue* V = e->cachedLvalue; - result = new DVarValue(e->type, V); - return; - } + ////////////////////////////////////////////////////////////////////////////////////////// - // handle magic inline asm - if (e->e1->op == TOKvar) - { - VarExp* ve = static_cast(e->e1); - if (FuncDeclaration* fd = ve->var->isFuncDeclaration()) - { - if (fd->llvmInternal == LLVMinline_asm) - { - result = DtoInlineAsmExpr(e->loc, fd, e->arguments); - return; - } - } - } + void visit(CastExp *e) { + IF_LOG Logger::print("CastExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // Check if we are about to construct a just declared temporary. DMD - // unfortunately rewrites this as - // MyStruct(myArgs) => (MyStruct tmp; tmp).this(myArgs), - // which would lead us to invoke the dtor even if the ctor throws. To - // work around this, we hold on to the cleanup and push it only after - // making the function call. - // - // The correct fix for this (DMD issue 13095) would have been to adapt - // the AST, but we are stuck with this as DMD also patched over it with - // a similar hack. - VarDeclaration* delayedDtorVar = NULL; - Expression* delayedDtorExp = NULL; - if (e->f && e->f->isCtorDeclaration() && e->e1->op == TOKdotvar) - { - DotVarExp* dve = static_cast(e->e1); - if (dve->e1->op == TOKcomma) - { - CommaExp* ce = static_cast(dve->e1); - if (ce->e1->op == TOKdeclaration && ce->e2->op == TOKvar) - { - VarExp* ve = static_cast(ce->e2); - if (VarDeclaration* vd = ve->var->isVarDeclaration()) - { - if (vd->edtor && !vd->noscope) - { - Logger::println("Delaying edtor"); - delayedDtorVar = vd; - delayedDtorExp = vd->edtor; - vd->edtor = NULL; - } - } - } - } - } + // get the value to cast + DValue *u = toElem(e->e1); - // get the callee value - DValue* fnval; - if (e->directcall) - { - // TODO: Do this as an extra parameter to DotVarExp implementation. - assert(e->e1->op == TOKdotvar); - DotVarExp* dve = static_cast(e->e1); - FuncDeclaration* fdecl = dve->var->isFuncDeclaration(); - assert(fdecl); - DtoResolveFunction(fdecl); - fnval = new DFuncValue(fdecl, getIrFunc(fdecl)->func, toElem(dve->e1)->getRVal()); - } - else - { - fnval = toElem(e->e1); - } - - // get func value if any - DFuncValue* dfnval = fnval->isFunc(); - - // handle magic intrinsics (mapping to instructions) - if (dfnval && dfnval->func) - { - FuncDeclaration* fndecl = dfnval->func; - - // as requested by bearophile, see if it's a C printf call and that it's valid. - if (global.params.warnings && checkPrintf) - { - if (fndecl->linkage == LINKc && strcmp(fndecl->ident->string, "printf") == 0) - { - warnInvalidPrintfCall(e->loc, (*e->arguments)[0], e->arguments->dim); - } - } - - if (DtoLowerMagicIntrinsic(p, fndecl, e, result)) - return; - } - - result = DtoCallFunction(e->loc, e->type, fnval, e->arguments); - - if (delayedDtorVar) - { - delayedDtorVar->edtor = delayedDtorExp; - pushVarDtorCleanup(p, delayedDtorVar); - } + // handle cast to void (usually created by frontend to avoid "has no effect" + // error) + if (e->to == Type::tvoid) { + result = new DImValue(Type::tvoid, + llvm::UndefValue::get(DtoMemType(Type::tvoid))); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + // cast it to the 'to' type, if necessary + result = u; + if (!e->to->equals(e->e1->type)) + result = DtoCast(e->loc, u, e->to); - void visit(CastExp *e) - { - IF_LOG Logger::print("CastExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + // paint the type, if necessary + if (!e->type->equals(e->to)) + result = DtoPaintType(e->loc, result, e->type); + } - // get the value to cast - DValue* u = toElem(e->e1); + ////////////////////////////////////////////////////////////////////////////////////////// - // handle cast to void (usually created by frontend to avoid "has no effect" error) - if (e->to == Type::tvoid) { - result = new DImValue(Type::tvoid, llvm::UndefValue::get(DtoMemType(Type::tvoid))); - return; - } + void visit(SymOffExp *e) { + IF_LOG Logger::print("SymOffExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // cast it to the 'to' type, if necessary - result = u; - if (!e->to->equals(e->e1->type)) - result = DtoCast(e->loc, u, e->to); + DValue *base = DtoSymbolAddress(e->loc, e->var->type, e->var); - // paint the type, if necessary - if (!e->type->equals(e->to)) - result = DtoPaintType(e->loc, result, e->type); + // This weird setup is required to be able to handle both variables as + // well as functions and TypeInfo references (which are not a DVarValue + // as well due to the level-of-indirection hack in Type::getTypeInfo that + // is unfortunately required by the frontend). + llvm::Value *baseValue; + if (base->isLVal()) + baseValue = base->getLVal(); + else + baseValue = base->getRVal(); + assert(isaPointer(baseValue)); + + llvm::Value *offsetValue; + Type *offsetType; + + if (e->offset == 0) { + offsetValue = baseValue; + offsetType = base->type->pointerTo(); + } else { + uint64_t elemSize = gDataLayout->getTypeAllocSize( + baseValue->getType()->getContainedType(0)); + if (e->offset % elemSize == 0) { + // We can turn this into a "nice" GEP. + offsetValue = DtoGEPi1(baseValue, e->offset / elemSize); + offsetType = base->type->pointerTo(); + } else { + // Offset isn't a multiple of base type size, just cast to i8* and + // apply the byte offset. + offsetValue = + DtoGEPi1(DtoBitCast(baseValue, getVoidPtrType()), e->offset); + offsetType = Type::tvoidptr; + } } - ////////////////////////////////////////////////////////////////////////////////////////// + // Casts are also "optimized into" SymOffExp by the frontend. + result = DtoCast(e->loc, new DImValue(offsetType, offsetValue), e->type); + } - void visit(SymOffExp *e) - { - IF_LOG Logger::print("SymOffExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////////////////////// - DValue* base = DtoSymbolAddress(e->loc, e->var->type, e->var); + void visit(AddrExp *e) { + IF_LOG Logger::println("AddrExp::toElem: %s @ %s", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // This weird setup is required to be able to handle both variables as - // well as functions and TypeInfo references (which are not a DVarValue - // as well due to the level-of-indirection hack in Type::getTypeInfo that - // is unfortunately required by the frontend). - llvm::Value* baseValue; - if (base->isLVal()) - baseValue = base->getLVal(); - else - baseValue = base->getRVal(); - assert(isaPointer(baseValue)); - - llvm::Value* offsetValue; - Type* offsetType; - - if (e->offset == 0) - { - offsetValue = baseValue; - offsetType = base->type->pointerTo(); - } - else - { - uint64_t elemSize = gDataLayout->getTypeAllocSize( - baseValue->getType()->getContainedType(0)); - if (e->offset % elemSize == 0) - { - // We can turn this into a "nice" GEP. - offsetValue = DtoGEPi1(baseValue, e->offset / elemSize); - offsetType = base->type->pointerTo(); - } - else - { - // Offset isn't a multiple of base type size, just cast to i8* and - // apply the byte offset. - offsetValue = DtoGEPi1(DtoBitCast(baseValue, getVoidPtrType()), e->offset); - offsetType = Type::tvoidptr; - } - } - - // Casts are also "optimized into" SymOffExp by the frontend. - result = DtoCast(e->loc, new DImValue(offsetType, offsetValue), e->type); + // The address of a StructLiteralExp can in fact be a global variable, check + // for that instead of re-codegening the literal. + if (e->e1->op == TOKstructliteral) { + // lvalue literal must be a global, hence we can just use + // toConstElem on the AddrExp to get the address. + LLConstant *addr = toConstElem(e, p); + IF_LOG Logger::cout() + << "returning address of struct literal global: " << addr << '\n'; + result = new DImValue(e->type, DtoBitCast(addr, DtoType(e->type))); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + DValue *v = toElem(e->e1, true); + if (v->isField()) { + Logger::println("is field"); + result = v; + return; + } else if (DFuncValue *fv = v->isFunc()) { + Logger::println("is func"); + // Logger::println("FuncDeclaration"); + FuncDeclaration *fd = fv->func; + assert(fd); + DtoResolveFunction(fd); + result = new DFuncValue(fd, getIrFunc(fd)->func); + return; + } else if (v->isIm()) { + Logger::println("is immediate"); + result = v; + return; + } + Logger::println("is nothing special"); - void visit(AddrExp *e) - { - IF_LOG Logger::println("AddrExp::toElem: %s @ %s", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - // The address of a StructLiteralExp can in fact be a global variable, check - // for that instead of re-codegening the literal. - if (e->e1->op == TOKstructliteral) - { - // lvalue literal must be a global, hence we can just use - // toConstElem on the AddrExp to get the address. - LLConstant *addr = toConstElem(e, p); - IF_LOG Logger::cout() << "returning address of struct literal global: " << - addr << '\n'; - result = new DImValue(e->type, DtoBitCast(addr, DtoType(e->type))); - return; - } - - DValue* v = toElem(e->e1, true); - if (v->isField()) { - Logger::println("is field"); - result = v; - return; - } - else if (DFuncValue* fv = v->isFunc()) { - Logger::println("is func"); - //Logger::println("FuncDeclaration"); - FuncDeclaration* fd = fv->func; - assert(fd); - DtoResolveFunction(fd); - result = new DFuncValue(fd, getIrFunc(fd)->func); - return; - } - else if (v->isIm()) { - Logger::println("is immediate"); - result = v; - return; - } - Logger::println("is nothing special"); - - // we special case here, since apparently taking the address of a slice is ok - LLValue* lval; - if (v->isLVal()) - lval = v->getLVal(); - else - { - assert(v->isSlice()); - lval = DtoAllocaDump(v, ".tmp_slice_storage"); - } - - IF_LOG Logger::cout() << "lval: " << *lval << '\n'; - result = new DImValue(e->type, DtoBitCast(lval, DtoType(e->type))); + // we special case here, since apparently taking the address of a slice is + // ok + LLValue *lval; + if (v->isLVal()) + lval = v->getLVal(); + else { + assert(v->isSlice()); + lval = DtoAllocaDump(v, ".tmp_slice_storage"); } - ////////////////////////////////////////////////////////////////////////////////////////// + IF_LOG Logger::cout() << "lval: " << *lval << '\n'; + result = new DImValue(e->type, DtoBitCast(lval, DtoType(e->type))); + } - void visit(PtrExp *e) - { - IF_LOG Logger::println("PtrExp::toElem: %s @ %s", e->toChars(), e->type->toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////////////////////// - // function pointers are special - if (e->type->toBasetype()->ty == Tfunction) - { - assert(!e->cachedLvalue); - DValue *dv = toElem(e->e1); - if (DFuncValue *dfv = dv->isFunc()) - result = new DFuncValue(e->type, dfv->func, dfv->getRVal()); - else - result = new DImValue(e->type, dv->getRVal()); - return; - } + void visit(PtrExp *e) { + IF_LOG Logger::println("PtrExp::toElem: %s @ %s", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // get the rvalue and return it as an lvalue - LLValue* V; - if (e->cachedLvalue) - { - V = e->cachedLvalue; - } - else - { - V = toElem(e->e1)->getRVal(); - } - - // The frontend emits dereferences of class/interfaces types to access the - // first member, which is the .classinfo property. - Type* origType = e->e1->type->toBasetype(); - if (origType->ty == Tclass) - { - TypeClass* ct = static_cast(origType); - - Type* resultType; - if (ct->sym->isInterfaceDeclaration()) - { - // For interfaces, the first entry in the vtbl is actually a pointer - // to an Interface instance, which has the type info as its first - // member, so we have to add an extra layer of indirection. - resultType = Type::typeinfointerface->type->pointerTo(); - } - else - { - resultType = Type::typeinfointerface->type; - } - - V = DtoBitCast(V, DtoType(resultType->pointerTo()->pointerTo())); - } - - result = new DVarValue(e->type, V); + // function pointers are special + if (e->type->toBasetype()->ty == Tfunction) { + assert(!e->cachedLvalue); + DValue *dv = toElem(e->e1); + if (DFuncValue *dfv = dv->isFunc()) + result = new DFuncValue(e->type, dfv->func, dfv->getRVal()); + else + result = new DImValue(e->type, dv->getRVal()); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(DotVarExp *e) - { - IF_LOG Logger::print("DotVarExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - if (e->cachedLvalue) - { - Logger::println("using cached lvalue"); - LLValue *V = e->cachedLvalue; - VarDeclaration* vd = e->var->isVarDeclaration(); - assert(vd); - result = new DVarValue(e->type, vd, V); - return; - } - - DValue* l = toElem(e->e1); - - Type* e1type = e->e1->type->toBasetype(); - - //Logger::println("e1type=%s", e1type->toChars()); - //Logger::cout() << *DtoType(e1type) << '\n'; - - if (VarDeclaration* vd = e->var->isVarDeclaration()) { - LLValue* arrptr; - // indexing struct pointer - if (e1type->ty == Tpointer) { - assert(e1type->nextOf()->ty == Tstruct); - TypeStruct* ts = static_cast(e1type->nextOf()); - arrptr = DtoIndexAggregate(l->getRVal(), ts->sym, vd); - } - // indexing normal struct - else if (e1type->ty == Tstruct) { - TypeStruct* ts = static_cast(e1type); - arrptr = DtoIndexAggregate(l->getRVal(), ts->sym, vd); - } - // indexing class - else if (e1type->ty == Tclass) { - TypeClass* tc = static_cast(e1type); - arrptr = DtoIndexAggregate(l->getRVal(), tc->sym, vd); - } - else - llvm_unreachable("Unknown DotVarExp type for VarDeclaration."); - - //Logger::cout() << "mem: " << *arrptr << '\n'; - result = new DVarValue(e->type, vd, arrptr); - } - else if (FuncDeclaration* fdecl = e->var->isFuncDeclaration()) - { - DtoResolveFunction(fdecl); - - // This is a bit more convoluted than it would need to be, because it - // has to take templated interface methods into account, for which - // isFinalFunc is not necessarily true. - // Also, private methods are always not virtual. - const bool nonFinal = !fdecl->isFinalFunc() && - (fdecl->isAbstract() || fdecl->isVirtual()) && - fdecl->prot().kind != PROTprivate; - - // Get the actual function value to call. - LLValue* funcval = 0; - if (nonFinal) - { - DImValue thisVal(e1type, l->getRVal()); - funcval = DtoVirtualFunctionPointer(&thisVal, fdecl, e->toChars()); - } - else - { - funcval = getIrFunc(fdecl)->func; - } - assert(funcval); - - result = new DFuncValue(fdecl, funcval, l->getRVal()); - } else { - llvm_unreachable("Unknown target for VarDeclaration."); - } + // get the rvalue and return it as an lvalue + LLValue *V; + if (e->cachedLvalue) { + V = e->cachedLvalue; + } else { + V = toElem(e->e1)->getRVal(); } - ////////////////////////////////////////////////////////////////////////////////////////// + // The frontend emits dereferences of class/interfaces types to access the + // first member, which is the .classinfo property. + Type *origType = e->e1->type->toBasetype(); + if (origType->ty == Tclass) { + TypeClass *ct = static_cast(origType); - void visit(ThisExp *e) - { - IF_LOG Logger::print("ThisExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + Type *resultType; + if (ct->sym->isInterfaceDeclaration()) { + // For interfaces, the first entry in the vtbl is actually a pointer + // to an Interface instance, which has the type info as its first + // member, so we have to add an extra layer of indirection. + resultType = Type::typeinfointerface->type->pointerTo(); + } else { + resultType = Type::typeinfointerface->type; + } - // special cases: `this(int) { this(); }` and `this(int) { super(); }` - if (!e->var) { - Logger::println("this exp without var declaration"); - LLValue* v = p->func()->thisArg; - result = new DVarValue(e->type, v); - return; - } - // regular this expr - else if (VarDeclaration* vd = e->var->isVarDeclaration()) { - LLValue* v; - Dsymbol* vdparent = vd->toParent2(); - Identifier *ident = p->func()->decl->ident; - // In D1, contracts are treated as normal nested methods, 'this' is - // just passed in the context struct along with any used parameters. - if (ident == Id::ensure || ident == Id::require) { - Logger::println("contract this exp"); - v = p->func()->nestArg; - v = DtoBitCast(v, DtoType(e->type)->getPointerTo()); - } else - if (vdparent != p->func()->decl) { - Logger::println("nested this exp"); - result = DtoNestedVariable(e->loc, e->type, vd, e->type->ty == Tstruct); - return; - } - else { - Logger::println("normal this exp"); - v = p->func()->thisArg; - } - result = new DVarValue(e->type, vd, v); - } else { - llvm_unreachable("No VarDeclaration in ThisExp."); - } + V = DtoBitCast(V, DtoType(resultType->pointerTo()->pointerTo())); } - ////////////////////////////////////////////////////////////////////////////////////////// + result = new DVarValue(e->type, V); + } - void visit(IndexExp *e) - { - IF_LOG Logger::print("IndexExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////////////////////// - if (e->cachedLvalue) - { - LLValue* V = e->cachedLvalue; - result = new DVarValue(e->type, V); - return; - } + void visit(DotVarExp *e) { + IF_LOG Logger::print("DotVarExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - DValue* l = toElem(e->e1); - - Type* e1type = e->e1->type->toBasetype(); - - p->arrays.push_back(l); // if $ is used it must be an array so this is fine. - DValue* r = toElem(e->e2); - p->arrays.pop_back(); - - LLValue* zero = DtoConstUint(0); - - LLValue* arrptr = 0; - if (e1type->ty == Tpointer) { - arrptr = DtoGEP1(l->getRVal(),r->getRVal()); - } - else if (e1type->ty == Tsarray) { - if (p->emitArrayBoundsChecks() && !e->indexIsInBounds) - DtoIndexBoundsCheck(e->loc, l, r); - arrptr = DtoGEP(l->getRVal(), zero, r->getRVal()); - } - else if (e1type->ty == Tarray) { - if (p->emitArrayBoundsChecks() && !e->indexIsInBounds) - DtoIndexBoundsCheck(e->loc, l, r); - arrptr = DtoArrayPtr(l); - arrptr = DtoGEP1(arrptr,r->getRVal()); - } - else if (e1type->ty == Taarray) { - result = DtoAAIndex(e->loc, e->type, l, r, e->modifiable); - return; - } - else { - IF_LOG Logger::println("e1type: %s", e1type->toChars()); - llvm_unreachable("Unknown IndexExp target."); - } - result = new DVarValue(e->type, arrptr); + if (e->cachedLvalue) { + Logger::println("using cached lvalue"); + LLValue *V = e->cachedLvalue; + VarDeclaration *vd = e->var->isVarDeclaration(); + assert(vd); + result = new DVarValue(e->type, vd, V); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + DValue *l = toElem(e->e1); - void visit(SliceExp *e) - { - IF_LOG Logger::print("SliceExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + Type *e1type = e->e1->type->toBasetype(); - // this is the new slicing code, it's different in that a full slice will no longer retain the original pointer. - // but this was broken if there *was* no original pointer, ie. a slice of a slice... - // now all slices have *both* the 'len' and 'ptr' fields set to != null. + // Logger::println("e1type=%s", e1type->toChars()); + // Logger::cout() << *DtoType(e1type) << '\n'; - // value being sliced - LLValue* elen = 0; - LLValue* eptr; - DValue* v = toElem(e->e1); + if (VarDeclaration *vd = e->var->isVarDeclaration()) { + LLValue *arrptr; + // indexing struct pointer + if (e1type->ty == Tpointer) { + assert(e1type->nextOf()->ty == Tstruct); + TypeStruct *ts = static_cast(e1type->nextOf()); + arrptr = DtoIndexAggregate(l->getRVal(), ts->sym, vd); + } + // indexing normal struct + else if (e1type->ty == Tstruct) { + TypeStruct *ts = static_cast(e1type); + arrptr = DtoIndexAggregate(l->getRVal(), ts->sym, vd); + } + // indexing class + else if (e1type->ty == Tclass) { + TypeClass *tc = static_cast(e1type); + arrptr = DtoIndexAggregate(l->getRVal(), tc->sym, vd); + } else + llvm_unreachable("Unknown DotVarExp type for VarDeclaration."); - // handle pointer slicing - Type* etype = e->e1->type->toBasetype(); - if (etype->ty == Tpointer) - { - assert(e->lwr); - eptr = v->getRVal(); - } - // array slice - else - { - eptr = DtoArrayPtr(v); - } + // Logger::cout() << "mem: " << *arrptr << '\n'; + result = new DVarValue(e->type, vd, arrptr); + } else if (FuncDeclaration *fdecl = e->var->isFuncDeclaration()) { + DtoResolveFunction(fdecl); - // has lower bound, pointer needs adjustment - if (e->lwr) - { - // must have upper bound too then - assert(e->upr); + // This is a bit more convoluted than it would need to be, because it + // has to take templated interface methods into account, for which + // isFinalFunc is not necessarily true. + // Also, private methods are always not virtual. + const bool nonFinal = !fdecl->isFinalFunc() && + (fdecl->isAbstract() || fdecl->isVirtual()) && + fdecl->prot().kind != PROTprivate; - // get bounds (make sure $ works) - p->arrays.push_back(v); - DValue* lo = toElem(e->lwr); - DValue* up = toElem(e->upr); - p->arrays.pop_back(); - LLValue* vlo = lo->getRVal(); - LLValue* vup = up->getRVal(); + // Get the actual function value to call. + LLValue *funcval = 0; + if (nonFinal) { + DImValue thisVal(e1type, l->getRVal()); + funcval = DtoVirtualFunctionPointer(&thisVal, fdecl, e->toChars()); + } else { + funcval = getIrFunc(fdecl)->func; + } + assert(funcval); - const bool needCheckUpper = (etype->ty != Tpointer) && - !e->upperIsInBounds; - const bool needCheckLower = !e->lowerIsLessThanUpper; - if (p->emitArrayBoundsChecks() && (needCheckUpper || needCheckLower)) - { - llvm::BasicBlock* failbb = llvm::BasicBlock::Create(p->context(), - "bounds.fail", p->topfunc()); - llvm::BasicBlock* okbb = llvm::BasicBlock::Create(p->context(), - "bounds.ok", p->topfunc()); + result = new DFuncValue(fdecl, funcval, l->getRVal()); + } else { + llvm_unreachable("Unknown target for VarDeclaration."); + } + } - llvm::Value *okCond = NULL; - if (needCheckUpper) - { - okCond = p->ir->CreateICmp(llvm::ICmpInst::ICMP_ULE, vup, - DtoArrayLen(v), "bounds.cmp.lo"); - } + ////////////////////////////////////////////////////////////////////////////////////////// - if (needCheckLower) - { - llvm::Value *cmp = p->ir->CreateICmp(llvm::ICmpInst::ICMP_ULE, - vlo, vup, "bounds.cmp.up"); - if (okCond) okCond = p->ir->CreateAnd(okCond, cmp); - else okCond = cmp; - } + void visit(ThisExp *e) { + IF_LOG Logger::print("ThisExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - p->ir->CreateCondBr(okCond, okbb, failbb); + // special cases: `this(int) { this(); }` and `this(int) { super(); }` + if (!e->var) { + Logger::println("this exp without var declaration"); + LLValue *v = p->func()->thisArg; + result = new DVarValue(e->type, v); + return; + } + // regular this expr + else if (VarDeclaration *vd = e->var->isVarDeclaration()) { + LLValue *v; + Dsymbol *vdparent = vd->toParent2(); + Identifier *ident = p->func()->decl->ident; + // In D1, contracts are treated as normal nested methods, 'this' is + // just passed in the context struct along with any used parameters. + if (ident == Id::ensure || ident == Id::require) { + Logger::println("contract this exp"); + v = p->func()->nestArg; + v = DtoBitCast(v, DtoType(e->type)->getPointerTo()); + } else if (vdparent != p->func()->decl) { + Logger::println("nested this exp"); + result = DtoNestedVariable(e->loc, e->type, vd, e->type->ty == Tstruct); + return; + } else { + Logger::println("normal this exp"); + v = p->func()->thisArg; + } + result = new DVarValue(e->type, vd, v); + } else { + llvm_unreachable("No VarDeclaration in ThisExp."); + } + } - p->scope() = IRScope(failbb); - DtoBoundsCheckFailCall(p, e->loc); + ////////////////////////////////////////////////////////////////////////////////////////// - p->scope() = IRScope(okbb); - } + void visit(IndexExp *e) { + IF_LOG Logger::print("IndexExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // offset by lower - eptr = DtoGEP1(eptr, vlo, "lowerbound"); - - // adjust length - elen = p->ir->CreateSub(vup, vlo); - } - // no bounds or full slice -> just convert to slice - else - { - assert(e->e1->type->toBasetype()->ty != Tpointer); - // if the sliceee is a static array, we use the length of that as DMD seems - // to give contrary inconsistent sizesin some multidimensional static array cases. - // (namely default initialization, int[16][16] arr; -> int[256] arr = 0;) - if (etype->ty == Tsarray) - { - TypeSArray* tsa = static_cast(etype); - elen = DtoConstSize_t(tsa->dim->toUInteger()); - - // in this case, we also need to make sure the pointer is cast to the innermost element type - eptr = DtoBitCast(eptr, DtoType(tsa->nextOf()->pointerTo())); - } - } - - // The frontend generates a SliceExp of static array type when assigning a - // fixed-width slice to a static array. - if (e->type->toBasetype()->ty == Tsarray) - { - LLValue *v = DtoBitCast(eptr, DtoType(e->type->pointerTo())); - result = new DVarValue(e->type, v); - return; - } - - if (!elen) elen = DtoArrayLen(v); - result = new DSliceValue(e->type, elen, eptr); + if (e->cachedLvalue) { + LLValue *V = e->cachedLvalue; + result = new DVarValue(e->type, V); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + DValue *l = toElem(e->e1); - void visit(CmpExp *e) - { - IF_LOG Logger::print("CmpExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + Type *e1type = e->e1->type->toBasetype(); - DValue* l = toElem(e->e1); - DValue* r = toElem(e->e2); + p->arrays.push_back(l); // if $ is used it must be an array so this is fine. + DValue *r = toElem(e->e2); + p->arrays.pop_back(); - Type* t = e->e1->type->toBasetype(); + LLValue *zero = DtoConstUint(0); - LLValue* eval = 0; + LLValue *arrptr = 0; + if (e1type->ty == Tpointer) { + arrptr = DtoGEP1(l->getRVal(), r->getRVal()); + } else if (e1type->ty == Tsarray) { + if (p->emitArrayBoundsChecks() && !e->indexIsInBounds) + DtoIndexBoundsCheck(e->loc, l, r); + arrptr = DtoGEP(l->getRVal(), zero, r->getRVal()); + } else if (e1type->ty == Tarray) { + if (p->emitArrayBoundsChecks() && !e->indexIsInBounds) + DtoIndexBoundsCheck(e->loc, l, r); + arrptr = DtoArrayPtr(l); + arrptr = DtoGEP1(arrptr, r->getRVal()); + } else if (e1type->ty == Taarray) { + result = DtoAAIndex(e->loc, e->type, l, r, e->modifiable); + return; + } else { + IF_LOG Logger::println("e1type: %s", e1type->toChars()); + llvm_unreachable("Unknown IndexExp target."); + } + result = new DVarValue(e->type, arrptr); + } - if (t->isintegral() || t->ty == Tpointer || t->ty == Tnull) - { - llvm::ICmpInst::Predicate icmpPred; - tokToIcmpPred(e->op, isLLVMUnsigned(t), &icmpPred, &eval); + ////////////////////////////////////////////////////////////////////////////////////////// - if (!eval) - { - LLValue* a = l->getRVal(); - LLValue* b = r->getRVal(); - IF_LOG { - Logger::cout() << "type 1: " << *a << '\n'; - Logger::cout() << "type 2: " << *b << '\n'; - } - if (a->getType() != b->getType()) - b = DtoBitCast(b, a->getType()); - eval = p->ir->CreateICmp(icmpPred, a, b); - } - } - else if (t->isfloating()) - { - llvm::FCmpInst::Predicate cmpop; - switch(e->op) - { - case TOKlt: - cmpop = llvm::FCmpInst::FCMP_OLT;break; - case TOKle: - cmpop = llvm::FCmpInst::FCMP_OLE;break; - case TOKgt: - cmpop = llvm::FCmpInst::FCMP_OGT;break; - case TOKge: - cmpop = llvm::FCmpInst::FCMP_OGE;break; - case TOKunord: - cmpop = llvm::FCmpInst::FCMP_UNO;break; - case TOKule: - cmpop = llvm::FCmpInst::FCMP_ULE;break; - case TOKul: - cmpop = llvm::FCmpInst::FCMP_ULT;break; - case TOKuge: - cmpop = llvm::FCmpInst::FCMP_UGE;break; - case TOKug: - cmpop = llvm::FCmpInst::FCMP_UGT;break; - case TOKue: - cmpop = llvm::FCmpInst::FCMP_UEQ;break; - case TOKlg: - cmpop = llvm::FCmpInst::FCMP_ONE;break; - case TOKleg: - cmpop = llvm::FCmpInst::FCMP_ORD;break; + void visit(SliceExp *e) { + IF_LOG Logger::print("SliceExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - default: - llvm_unreachable("Unsupported floating point comparison operator."); - } - eval = p->ir->CreateFCmp(cmpop, l->getRVal(), r->getRVal()); - } - else if (t->ty == Tsarray || t->ty == Tarray) - { - Logger::println("static or dynamic array"); - eval = DtoArrayCompare(e->loc, e->op, l, r); - } - else if (t->ty == Taarray) - { - eval = LLConstantInt::getFalse(gIR->context()); - } - else if (t->ty == Tdelegate) - { - llvm::ICmpInst::Predicate icmpPred; - tokToIcmpPred(e->op, isLLVMUnsigned(t), &icmpPred, &eval); + // this is the new slicing code, it's different in that a full slice will no + // longer retain the original pointer. + // but this was broken if there *was* no original pointer, ie. a slice of a + // slice... + // now all slices have *both* the 'len' and 'ptr' fields set to != null. - if (!eval) - { - // First compare the function pointers, then the context ones. This is - // what DMD does. - llvm::Value* lhs = l->getRVal(); - llvm::Value* rhs = r->getRVal(); + // value being sliced + LLValue *elen = 0; + LLValue *eptr; + DValue *v = toElem(e->e1); - llvm::BasicBlock* fptreq = llvm::BasicBlock::Create( - gIR->context(), "fptreq", gIR->topfunc()); - llvm::BasicBlock* fptrneq = llvm::BasicBlock::Create( - gIR->context(), "fptrneq", gIR->topfunc()); - llvm::BasicBlock* dgcmpend = llvm::BasicBlock::Create( - gIR->context(), "dgcmpend", gIR->topfunc()); - - llvm::Value* lfptr = p->ir->CreateExtractValue(lhs, 1, ".lfptr"); - llvm::Value* rfptr = p->ir->CreateExtractValue(rhs, 1, ".rfptr"); - - llvm::Value* fptreqcmp = p->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, - lfptr, rfptr, ".fptreqcmp"); - llvm::BranchInst::Create(fptreq, fptrneq, fptreqcmp, p->scopebb()); - - p->scope() = IRScope(fptreq); - llvm::Value* lctx = p->ir->CreateExtractValue(lhs, 0, ".lctx"); - llvm::Value* rctx = p->ir->CreateExtractValue(rhs, 0, ".rctx"); - llvm::Value* ctxcmp = p->ir->CreateICmp(icmpPred, lctx, rctx, ".ctxcmp"); - llvm::BranchInst::Create(dgcmpend,p->scopebb()); - - p->scope() = IRScope(fptrneq); - llvm::Value* fptrcmp = p->ir->CreateICmp(icmpPred, lfptr, rfptr, ".fptrcmp"); - llvm::BranchInst::Create(dgcmpend,p->scopebb()); - - p->scope() = IRScope(dgcmpend); - llvm::PHINode* phi = p->ir->CreatePHI(ctxcmp->getType(), 2, ".dgcmp"); - phi->addIncoming(ctxcmp, fptreq); - phi->addIncoming(fptrcmp, fptrneq); - eval = phi; - } - } - else - { - llvm_unreachable("Unsupported CmpExp type"); - } - - result = new DImValue(e->type, eval); + // handle pointer slicing + Type *etype = e->e1->type->toBasetype(); + if (etype->ty == Tpointer) { + assert(e->lwr); + eptr = v->getRVal(); + } + // array slice + else { + eptr = DtoArrayPtr(v); } - ////////////////////////////////////////////////////////////////////////////////////////// + // has lower bound, pointer needs adjustment + if (e->lwr) { + // must have upper bound too then + assert(e->upr); - void visit(EqualExp *e) - { - IF_LOG Logger::print("EqualExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + // get bounds (make sure $ works) + p->arrays.push_back(v); + DValue *lo = toElem(e->lwr); + DValue *up = toElem(e->upr); + p->arrays.pop_back(); + LLValue *vlo = lo->getRVal(); + LLValue *vup = up->getRVal(); - DValue* l = toElem(e->e1); - DValue* r = toElem(e->e2); - LLValue* lv = l->getRVal(); - LLValue* rv = r->getRVal(); + const bool needCheckUpper = + (etype->ty != Tpointer) && !e->upperIsInBounds; + const bool needCheckLower = !e->lowerIsLessThanUpper; + if (p->emitArrayBoundsChecks() && (needCheckUpper || needCheckLower)) { + llvm::BasicBlock *failbb = + llvm::BasicBlock::Create(p->context(), "bounds.fail", p->topfunc()); + llvm::BasicBlock *okbb = + llvm::BasicBlock::Create(p->context(), "bounds.ok", p->topfunc()); - Type* t = e->e1->type->toBasetype(); - - LLValue* eval = 0; - - // the Tclass catches interface comparisons, regular - // class equality should be rewritten as a.opEquals(b) by this time - if (t->isintegral() || t->ty == Tpointer || t->ty == Tclass || t->ty == Tnull) - { - Logger::println("integral or pointer or interface"); - llvm::ICmpInst::Predicate cmpop; - switch(e->op) - { - case TOKequal: - cmpop = llvm::ICmpInst::ICMP_EQ; - break; - case TOKnotequal: - cmpop = llvm::ICmpInst::ICMP_NE; - break; - default: - llvm_unreachable("Unsupported integral type equality comparison."); - } - if (rv->getType() != lv->getType()) { - rv = DtoBitCast(rv, lv->getType()); - } - IF_LOG { - Logger::cout() << "lv: " << *lv << '\n'; - Logger::cout() << "rv: " << *rv << '\n'; - } - eval = p->ir->CreateICmp(cmpop, lv, rv); - } - else if (t->isfloating()) // includes iscomplex - { - eval = DtoBinNumericEquals(e->loc, l, r, e->op); - } - else if (t->ty == Tsarray || t->ty == Tarray) - { - Logger::println("static or dynamic array"); - eval = DtoArrayEquals(e->loc, e->op, l, r); - } - else if (t->ty == Taarray) - { - Logger::println("associative array"); - eval = DtoAAEquals(e->loc, e->op, l, r); - } - else if (t->ty == Tdelegate) - { - Logger::println("delegate"); - eval = DtoDelegateEquals(e->op, l->getRVal(), r->getRVal()); - } - else if (t->ty == Tstruct) - { - Logger::println("struct"); - // when this is reached it means there is no opEquals overload. - eval = DtoStructEquals(e->op, l, r); - } - else - { - llvm_unreachable("Unsupported EqualExp type."); + llvm::Value *okCond = NULL; + if (needCheckUpper) { + okCond = p->ir->CreateICmp(llvm::ICmpInst::ICMP_ULE, vup, + DtoArrayLen(v), "bounds.cmp.lo"); } - result = new DImValue(e->type, eval); + if (needCheckLower) { + llvm::Value *cmp = p->ir->CreateICmp(llvm::ICmpInst::ICMP_ULE, vlo, + vup, "bounds.cmp.up"); + if (okCond) + okCond = p->ir->CreateAnd(okCond, cmp); + else + okCond = cmp; + } + + p->ir->CreateCondBr(okCond, okbb, failbb); + + p->scope() = IRScope(failbb); + DtoBoundsCheckFailCall(p, e->loc); + + p->scope() = IRScope(okbb); + } + + // offset by lower + eptr = DtoGEP1(eptr, vlo, "lowerbound"); + + // adjust length + elen = p->ir->CreateSub(vup, vlo); + } + // no bounds or full slice -> just convert to slice + else { + assert(e->e1->type->toBasetype()->ty != Tpointer); + // if the sliceee is a static array, we use the length of that as DMD + // seems + // to give contrary inconsistent sizesin some multidimensional static + // array cases. + // (namely default initialization, int[16][16] arr; -> int[256] arr = 0;) + if (etype->ty == Tsarray) { + TypeSArray *tsa = static_cast(etype); + elen = DtoConstSize_t(tsa->dim->toUInteger()); + + // in this case, we also need to make sure the pointer is cast to the + // innermost element type + eptr = DtoBitCast(eptr, DtoType(tsa->nextOf()->pointerTo())); + } } - ////////////////////////////////////////////////////////////////////////////////////////// + // The frontend generates a SliceExp of static array type when assigning a + // fixed-width slice to a static array. + if (e->type->toBasetype()->ty == Tsarray) { + LLValue *v = DtoBitCast(eptr, DtoType(e->type->pointerTo())); + result = new DVarValue(e->type, v); + return; + } - void visit(PostExp *e) - { - IF_LOG Logger::print("PostExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + if (!elen) + elen = DtoArrayLen(v); + result = new DSliceValue(e->type, elen, eptr); + } - DValue* l = toElem(e->e1); - toElem(e->e2); + ////////////////////////////////////////////////////////////////////////////////////////// - LLValue* val = l->getRVal(); - LLValue* post = 0; + void visit(CmpExp *e) { + IF_LOG Logger::print("CmpExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - Type* e1type = e->e1->type->toBasetype(); - Type* e2type = e->e2->type->toBasetype(); + DValue *l = toElem(e->e1); + DValue *r = toElem(e->e2); - if (e1type->isintegral()) - { - assert(e2type->isintegral()); - LLValue* one = LLConstantInt::get(val->getType(), 1, !e2type->isunsigned()); - if (e->op == TOKplusplus) { - post = llvm::BinaryOperator::CreateAdd(val, one, "", p->scopebb()); - } - else if (e->op == TOKminusminus) { - post = llvm::BinaryOperator::CreateSub(val, one, "", p->scopebb()); - } + Type *t = e->e1->type->toBasetype(); + + LLValue *eval = 0; + + if (t->isintegral() || t->ty == Tpointer || t->ty == Tnull) { + llvm::ICmpInst::Predicate icmpPred; + tokToIcmpPred(e->op, isLLVMUnsigned(t), &icmpPred, &eval); + + if (!eval) { + LLValue *a = l->getRVal(); + LLValue *b = r->getRVal(); + IF_LOG { + Logger::cout() << "type 1: " << *a << '\n'; + Logger::cout() << "type 2: " << *b << '\n'; } - else if (e1type->ty == Tpointer) - { - assert(e->e2->op == TOKint64); - LLConstant *offset; - if (e->op == TOKplusplus) - offset = LLConstantInt::get(DtoSize_t(), static_cast(1), false); - else - offset = LLConstantInt::get(DtoSize_t(), static_cast(-1), true); - post = llvm::GetElementPtrInst::Create( + if (a->getType() != b->getType()) + b = DtoBitCast(b, a->getType()); + eval = p->ir->CreateICmp(icmpPred, a, b); + } + } else if (t->isfloating()) { + llvm::FCmpInst::Predicate cmpop; + switch (e->op) { + case TOKlt: + cmpop = llvm::FCmpInst::FCMP_OLT; + break; + case TOKle: + cmpop = llvm::FCmpInst::FCMP_OLE; + break; + case TOKgt: + cmpop = llvm::FCmpInst::FCMP_OGT; + break; + case TOKge: + cmpop = llvm::FCmpInst::FCMP_OGE; + break; + case TOKunord: + cmpop = llvm::FCmpInst::FCMP_UNO; + break; + case TOKule: + cmpop = llvm::FCmpInst::FCMP_ULE; + break; + case TOKul: + cmpop = llvm::FCmpInst::FCMP_ULT; + break; + case TOKuge: + cmpop = llvm::FCmpInst::FCMP_UGE; + break; + case TOKug: + cmpop = llvm::FCmpInst::FCMP_UGT; + break; + case TOKue: + cmpop = llvm::FCmpInst::FCMP_UEQ; + break; + case TOKlg: + cmpop = llvm::FCmpInst::FCMP_ONE; + break; + case TOKleg: + cmpop = llvm::FCmpInst::FCMP_ORD; + break; + + default: + llvm_unreachable("Unsupported floating point comparison operator."); + } + eval = p->ir->CreateFCmp(cmpop, l->getRVal(), r->getRVal()); + } else if (t->ty == Tsarray || t->ty == Tarray) { + Logger::println("static or dynamic array"); + eval = DtoArrayCompare(e->loc, e->op, l, r); + } else if (t->ty == Taarray) { + eval = LLConstantInt::getFalse(gIR->context()); + } else if (t->ty == Tdelegate) { + llvm::ICmpInst::Predicate icmpPred; + tokToIcmpPred(e->op, isLLVMUnsigned(t), &icmpPred, &eval); + + if (!eval) { + // First compare the function pointers, then the context ones. This is + // what DMD does. + llvm::Value *lhs = l->getRVal(); + llvm::Value *rhs = r->getRVal(); + + llvm::BasicBlock *fptreq = + llvm::BasicBlock::Create(gIR->context(), "fptreq", gIR->topfunc()); + llvm::BasicBlock *fptrneq = + llvm::BasicBlock::Create(gIR->context(), "fptrneq", gIR->topfunc()); + llvm::BasicBlock *dgcmpend = llvm::BasicBlock::Create( + gIR->context(), "dgcmpend", gIR->topfunc()); + + llvm::Value *lfptr = p->ir->CreateExtractValue(lhs, 1, ".lfptr"); + llvm::Value *rfptr = p->ir->CreateExtractValue(rhs, 1, ".rfptr"); + + llvm::Value *fptreqcmp = p->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, + lfptr, rfptr, ".fptreqcmp"); + llvm::BranchInst::Create(fptreq, fptrneq, fptreqcmp, p->scopebb()); + + p->scope() = IRScope(fptreq); + llvm::Value *lctx = p->ir->CreateExtractValue(lhs, 0, ".lctx"); + llvm::Value *rctx = p->ir->CreateExtractValue(rhs, 0, ".rctx"); + llvm::Value *ctxcmp = + p->ir->CreateICmp(icmpPred, lctx, rctx, ".ctxcmp"); + llvm::BranchInst::Create(dgcmpend, p->scopebb()); + + p->scope() = IRScope(fptrneq); + llvm::Value *fptrcmp = + p->ir->CreateICmp(icmpPred, lfptr, rfptr, ".fptrcmp"); + llvm::BranchInst::Create(dgcmpend, p->scopebb()); + + p->scope() = IRScope(dgcmpend); + llvm::PHINode *phi = p->ir->CreatePHI(ctxcmp->getType(), 2, ".dgcmp"); + phi->addIncoming(ctxcmp, fptreq); + phi->addIncoming(fptrcmp, fptrneq); + eval = phi; + } + } else { + llvm_unreachable("Unsupported CmpExp type"); + } + + result = new DImValue(e->type, eval); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(EqualExp *e) { + IF_LOG Logger::print("EqualExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *l = toElem(e->e1); + DValue *r = toElem(e->e2); + LLValue *lv = l->getRVal(); + LLValue *rv = r->getRVal(); + + Type *t = e->e1->type->toBasetype(); + + LLValue *eval = 0; + + // the Tclass catches interface comparisons, regular + // class equality should be rewritten as a.opEquals(b) by this time + if (t->isintegral() || t->ty == Tpointer || t->ty == Tclass || + t->ty == Tnull) { + Logger::println("integral or pointer or interface"); + llvm::ICmpInst::Predicate cmpop; + switch (e->op) { + case TOKequal: + cmpop = llvm::ICmpInst::ICMP_EQ; + break; + case TOKnotequal: + cmpop = llvm::ICmpInst::ICMP_NE; + break; + default: + llvm_unreachable("Unsupported integral type equality comparison."); + } + if (rv->getType() != lv->getType()) { + rv = DtoBitCast(rv, lv->getType()); + } + IF_LOG { + Logger::cout() << "lv: " << *lv << '\n'; + Logger::cout() << "rv: " << *rv << '\n'; + } + eval = p->ir->CreateICmp(cmpop, lv, rv); + } else if (t->isfloating()) // includes iscomplex + { + eval = DtoBinNumericEquals(e->loc, l, r, e->op); + } else if (t->ty == Tsarray || t->ty == Tarray) { + Logger::println("static or dynamic array"); + eval = DtoArrayEquals(e->loc, e->op, l, r); + } else if (t->ty == Taarray) { + Logger::println("associative array"); + eval = DtoAAEquals(e->loc, e->op, l, r); + } else if (t->ty == Tdelegate) { + Logger::println("delegate"); + eval = DtoDelegateEquals(e->op, l->getRVal(), r->getRVal()); + } else if (t->ty == Tstruct) { + Logger::println("struct"); + // when this is reached it means there is no opEquals overload. + eval = DtoStructEquals(e->op, l, r); + } else { + llvm_unreachable("Unsupported EqualExp type."); + } + + result = new DImValue(e->type, eval); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(PostExp *e) { + IF_LOG Logger::print("PostExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *l = toElem(e->e1); + toElem(e->e2); + + LLValue *val = l->getRVal(); + LLValue *post = 0; + + Type *e1type = e->e1->type->toBasetype(); + Type *e2type = e->e2->type->toBasetype(); + + if (e1type->isintegral()) { + assert(e2type->isintegral()); + LLValue *one = + LLConstantInt::get(val->getType(), 1, !e2type->isunsigned()); + if (e->op == TOKplusplus) { + post = llvm::BinaryOperator::CreateAdd(val, one, "", p->scopebb()); + } else if (e->op == TOKminusminus) { + post = llvm::BinaryOperator::CreateSub(val, one, "", p->scopebb()); + } + } else if (e1type->ty == Tpointer) { + assert(e->e2->op == TOKint64); + LLConstant *offset; + if (e->op == TOKplusplus) + offset = + LLConstantInt::get(DtoSize_t(), static_cast(1), false); + else + offset = + LLConstantInt::get(DtoSize_t(), static_cast(-1), true); + post = llvm::GetElementPtrInst::Create( #if LDC_LLVM_VER >= 307 - isaPointer(val)->getElementType(), + isaPointer(val)->getElementType(), #endif - val, offset, "", p->scopebb()); - } - else if (e1type->isfloating()) - { - assert(e2type->isfloating()); - LLValue* one = DtoConstFP(e1type, ldouble(1.0)); - if (e->op == TOKplusplus) { - post = llvm::BinaryOperator::CreateFAdd(val,one, "", p->scopebb()); - } - else if (e->op == TOKminusminus) { - post = llvm::BinaryOperator::CreateFSub(val,one, "", p->scopebb()); - } - } - else - assert(post); + val, offset, "", p->scopebb()); + } else if (e1type->isfloating()) { + assert(e2type->isfloating()); + LLValue *one = DtoConstFP(e1type, ldouble(1.0)); + if (e->op == TOKplusplus) { + post = llvm::BinaryOperator::CreateFAdd(val, one, "", p->scopebb()); + } else if (e->op == TOKminusminus) { + post = llvm::BinaryOperator::CreateFSub(val, one, "", p->scopebb()); + } + } else + assert(post); - DtoStore(post, l->getLVal()); - result = new DImValue(e->type, val); + DtoStore(post, l->getLVal()); + result = new DImValue(e->type, val); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(NewExp *e) { + IF_LOG Logger::print("NewExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + bool isArgprefixHandled = false; + + assert(e->newtype); + Type *ntype = e->newtype->toBasetype(); + + // new class + if (ntype->ty == Tclass) { + Logger::println("new class"); + result = DtoNewClass(e->loc, static_cast(ntype), e); + isArgprefixHandled = true; // by DtoNewClass() + } + // new dynamic array + else if (ntype->ty == Tarray) { + IF_LOG Logger::println("new dynamic array: %s", e->newtype->toChars()); + assert(e->argprefix == NULL); + // get dim + assert(e->arguments); + assert(e->arguments->dim >= 1); + if (e->arguments->dim == 1) { + DValue *sz = toElem((*e->arguments)[0]); + // allocate & init + result = DtoNewDynArray(e->loc, e->newtype, sz, true); + } else { + size_t ndims = e->arguments->dim; + std::vector dims; + dims.reserve(ndims); + for (size_t i = 0; i < ndims; ++i) + dims.push_back(toElem((*e->arguments)[i])); + result = DtoNewMulDimDynArray(e->loc, e->newtype, &dims[0], ndims); + } + } + // new static array + else if (ntype->ty == Tsarray) { + llvm_unreachable("Static array new should decay to dynamic array."); + } + // new struct + else if (ntype->ty == Tstruct) { + IF_LOG Logger::println("new struct on heap: %s\n", e->newtype->toChars()); + + TypeStruct *ts = static_cast(ntype); + + // allocate + LLValue *mem = 0; + if (e->allocator) { + // custom allocator + DtoResolveFunction(e->allocator); + DFuncValue dfn(e->allocator, getIrFunc(e->allocator)->func); + DValue *res = DtoCallFunction(e->loc, NULL, &dfn, e->newargs); + mem = DtoBitCast(res->getRVal(), DtoType(ntype->pointerTo()), + ".newstruct_custom"); + } else { + // default allocator + mem = DtoNewStruct(e->loc, ts); + } + + if (!e->member && e->arguments) { + IF_LOG Logger::println("Constructing using literal"); + write_struct_literal(e->loc, mem, ts->sym, e->arguments); + } else { + // set nested context + if (ts->sym->isNested() && ts->sym->vthis) + DtoResolveNestedContext(e->loc, ts->sym, mem); + + // call constructor + if (e->member) { + // evaluate argprefix + if (e->argprefix) { + toElemDtor(e->argprefix); + isArgprefixHandled = true; + } + + IF_LOG Logger::println("Calling constructor"); + assert(e->arguments != NULL); + DtoResolveFunction(e->member); + DFuncValue dfn(e->member, getIrFunc(e->member)->func, mem); + DtoCallFunction(e->loc, ts, &dfn, e->arguments); + } + } + + result = new DImValue(e->type, mem); + } + // new basic type + else { + IF_LOG Logger::println("basic type on heap: %s\n", e->newtype->toChars()); + assert(e->argprefix == NULL); + + // allocate + LLValue *mem = DtoNew(e->loc, e->newtype); + DVarValue tmpvar(e->newtype, mem); + + Expression *exp = 0; + if (!e->arguments || e->arguments->dim == 0) { + IF_LOG Logger::println("default initializer\n"); + // static arrays never appear here, so using the defaultInit is ok! + exp = e->newtype->defaultInit(e->loc); + } else { + IF_LOG Logger::println("uniform constructor\n"); + assert(e->arguments->dim == 1); + exp = (*e->arguments)[0]; + } + + DValue *iv = toElem(exp); + DtoAssign(e->loc, &tmpvar, iv); + + // return as pointer-to + result = new DImValue(e->type, mem); } - ////////////////////////////////////////////////////////////////////////////////////////// + assert(e->argprefix == NULL || isArgprefixHandled); + } - void visit(NewExp *e) - { - IF_LOG Logger::print("NewExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////////////////////// - bool isArgprefixHandled = false; + void visit(DeleteExp *e) { + IF_LOG Logger::print("DeleteExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - assert(e->newtype); - Type* ntype = e->newtype->toBasetype(); + DValue *dval = toElem(e->e1); + Type *et = e->e1->type->toBasetype(); - // new class - if (ntype->ty == Tclass) { - Logger::println("new class"); - result = DtoNewClass(e->loc, static_cast(ntype), e); - isArgprefixHandled = true; // by DtoNewClass() + // pointer + if (et->ty == Tpointer) { + Type *elementType = et->nextOf()->toBasetype(); + if (elementType->ty == Tstruct && elementType->needsDestruction()) + DtoDeleteStruct(e->loc, dval); + else + DtoDeleteMemory(e->loc, dval); + } + // class + else if (et->ty == Tclass) { + bool onstack = false; + TypeClass *tc = static_cast(et); + if (tc->sym->isInterfaceDeclaration()) { + DtoDeleteInterface(e->loc, dval); + onstack = true; + } else if (DVarValue *vv = dval->isVar()) { + if (vv->var && vv->var->onstack) { + DtoFinalizeClass(e->loc, dval->getRVal()); + onstack = true; } - // new dynamic array - else if (ntype->ty == Tarray) - { - IF_LOG Logger::println("new dynamic array: %s", e->newtype->toChars()); - assert(e->argprefix == NULL); - // get dim - assert(e->arguments); - assert(e->arguments->dim >= 1); - if (e->arguments->dim == 1) - { - DValue* sz = toElem((*e->arguments)[0]); - // allocate & init - result = DtoNewDynArray(e->loc, e->newtype, sz, true); - } - else - { - size_t ndims = e->arguments->dim; - std::vector dims; - dims.reserve(ndims); - for (size_t i=0; iarguments)[i])); - result = DtoNewMulDimDynArray(e->loc, e->newtype, &dims[0], ndims); - } - } - // new static array - else if (ntype->ty == Tsarray) - { - llvm_unreachable("Static array new should decay to dynamic array."); - } - // new struct - else if (ntype->ty == Tstruct) - { - IF_LOG Logger::println("new struct on heap: %s\n", e->newtype->toChars()); + } - TypeStruct* ts = static_cast(ntype); + if (!onstack) + DtoDeleteClass(e->loc, dval); // sets dval to null + else if (dval->isVar()) { + LLValue *lval = dval->getLVal(); + DtoStore(LLConstant::getNullValue(lval->getType()->getContainedType(0)), + lval); + } + } + // dyn array + else if (et->ty == Tarray) { + DtoDeleteArray(e->loc, dval); + if (dval->isLVal()) + DtoSetArrayToNull(dval->getLVal()); + } + // unknown/invalid + else { + llvm_unreachable("Unsupported DeleteExp target."); + } + } - // allocate - LLValue* mem = 0; - if (e->allocator) - { - // custom allocator - DtoResolveFunction(e->allocator); - DFuncValue dfn(e->allocator, getIrFunc(e->allocator)->func); - DValue* res = DtoCallFunction(e->loc, NULL, &dfn, e->newargs); - mem = DtoBitCast(res->getRVal(), DtoType(ntype->pointerTo()), ".newstruct_custom"); - } - else - { - // default allocator - mem = DtoNewStruct(e->loc, ts); - } + ////////////////////////////////////////////////////////////////////////////////////////// - if (!e->member && e->arguments) - { - IF_LOG Logger::println("Constructing using literal"); - write_struct_literal(e->loc, mem, ts->sym, e->arguments); - } - else - { - // set nested context - if (ts->sym->isNested() && ts->sym->vthis) - DtoResolveNestedContext(e->loc, ts->sym, mem); + void visit(ArrayLengthExp *e) { + IF_LOG Logger::print("ArrayLengthExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // call constructor - if (e->member) - { - // evaluate argprefix - if (e->argprefix) - { - toElemDtor(e->argprefix); - isArgprefixHandled = true; - } + DValue *u = toElem(e->e1); + result = new DImValue(e->type, DtoArrayLen(u)); + } - IF_LOG Logger::println("Calling constructor"); - assert(e->arguments != NULL); - DtoResolveFunction(e->member); - DFuncValue dfn(e->member, getIrFunc(e->member)->func, mem); - DtoCallFunction(e->loc, ts, &dfn, e->arguments); - } - } + ////////////////////////////////////////////////////////////////////////////////////////// - result = new DImValue(e->type, mem); - } - // new basic type - else - { - IF_LOG Logger::println("basic type on heap: %s\n", e->newtype->toChars()); - assert(e->argprefix == NULL); + void visit(AssertExp *e) { + IF_LOG Logger::print("AssertExp::toElem: %s\n", e->toChars()); + LOG_SCOPE; - // allocate - LLValue* mem = DtoNew(e->loc, e->newtype); - DVarValue tmpvar(e->newtype, mem); + // DMD allows syntax like this: + // f() == 0 || assert(false) + result = new DImValue(e->type, DtoConstBool(false)); - Expression* exp = 0; - if (!e->arguments || e->arguments->dim == 0) - { - IF_LOG Logger::println("default initializer\n"); - // static arrays never appear here, so using the defaultInit is ok! - exp = e->newtype->defaultInit(e->loc); - } - else - { - IF_LOG Logger::println("uniform constructor\n"); - assert(e->arguments->dim == 1); - exp = (*e->arguments)[0]; - } + if (!global.params.useAssert) + return; - DValue* iv = toElem(exp); - DtoAssign(e->loc, &tmpvar, iv); + // condition + DValue *cond; + Type *condty; - // return as pointer-to - result = new DImValue(e->type, mem); - } - - assert(e->argprefix == NULL || isArgprefixHandled); + // special case for dmd generated assert(this); when not in -release mode + if (e->e1->op == TOKthis && static_cast(e->e1)->var == NULL) { + LLValue *thisarg = p->func()->thisArg; + assert(thisarg && "null thisarg, but we're in assert(this) exp;"); + LLValue *thisptr = DtoLoad(thisarg); + condty = e->e1->type->toBasetype(); + cond = new DImValue(condty, thisptr); + } else { + cond = toElem(e->e1); + condty = e->e1->type->toBasetype(); } - ////////////////////////////////////////////////////////////////////////////////////////// + // create basic blocks + llvm::BasicBlock *passedbb = + llvm::BasicBlock::Create(gIR->context(), "assertPassed", p->topfunc()); + llvm::BasicBlock *failedbb = + llvm::BasicBlock::Create(gIR->context(), "assertFailed", p->topfunc()); - void visit(DeleteExp *e) - { - IF_LOG Logger::print("DeleteExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + // test condition + LLValue *condval = DtoCast(e->loc, cond, Type::tbool)->getRVal(); - DValue* dval = toElem(e->e1); - Type* et = e->e1->type->toBasetype(); + // branch + llvm::BranchInst::Create(passedbb, failedbb, condval, p->scopebb()); - // pointer - if (et->ty == Tpointer) - { - Type* elementType = et->nextOf()->toBasetype(); - if (elementType->ty == Tstruct && elementType->needsDestruction()) - DtoDeleteStruct(e->loc, dval); - else - DtoDeleteMemory(e->loc, dval); - } - // class - else if (et->ty == Tclass) - { - bool onstack = false; - TypeClass* tc = static_cast(et); - if (tc->sym->isInterfaceDeclaration()) - { - DtoDeleteInterface(e->loc, dval); - onstack = true; - } - else if (DVarValue* vv = dval->isVar()) { - if (vv->var && vv->var->onstack) { - DtoFinalizeClass(e->loc, dval->getRVal()); - onstack = true; - } - } + // failed: call assert runtime function + p->scope() = IRScope(failedbb); - if (!onstack) - DtoDeleteClass(e->loc, dval); // sets dval to null - else if (dval->isVar()) { - LLValue* lval = dval->getLVal(); - DtoStore(LLConstant::getNullValue(lval->getType()->getContainedType(0)), lval); - } - } - // dyn array - else if (et->ty == Tarray) - { - DtoDeleteArray(e->loc, dval); - if (dval->isLVal()) - DtoSetArrayToNull(dval->getLVal()); - } - // unknown/invalid - else - { - llvm_unreachable("Unsupported DeleteExp target."); - } + /* DMD Bugzilla 8360: If the condition is evaluated to true, + * 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) : NULL); + + // passed: + p->scope() = IRScope(passedbb); + + FuncDeclaration *invdecl; + // class invariants + if (global.params.useInvariants && condty->ty == Tclass && + !(static_cast(condty)->sym->isInterfaceDeclaration()) && + !(static_cast(condty)->sym->isCPPclass())) { + Logger::println("calling class invariant"); + llvm::Function *fn = LLVM_D_GetRuntimeFunction( + e->loc, gIR->module, + gABI->mangleForLLVM("_D9invariant12_d_invariantFC6ObjectZv", LINKd) + .c_str()); + LLValue *arg = + DtoBitCast(cond->getRVal(), fn->getFunctionType()->getParamType(0)); + gIR->CreateCallOrInvoke(fn, arg); + } + // struct invariants + else if (global.params.useInvariants && condty->ty == Tpointer && + condty->nextOf()->ty == Tstruct && + (invdecl = static_cast(condty->nextOf()) + ->sym->inv) != NULL) { + Logger::print("calling struct invariant"); + DtoResolveFunction(invdecl); + DFuncValue invfunc(invdecl, getIrFunc(invdecl)->func, cond->getRVal()); + DtoCallFunction(e->loc, NULL, &invfunc, NULL); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(NotExp *e) { + IF_LOG Logger::print("NotExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *u = toElem(e->e1); + + LLValue *b = DtoCast(e->loc, u, Type::tbool)->getRVal(); + + LLConstant *zero = DtoConstBool(false); + b = p->ir->CreateICmpEQ(b, zero); + + result = new DImValue(e->type, b); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(AndAndExp *e) { + IF_LOG Logger::print("AndAndExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *u = toElem(e->e1); + + llvm::BasicBlock *andand = + llvm::BasicBlock::Create(gIR->context(), "andand", gIR->topfunc()); + llvm::BasicBlock *andandend = + llvm::BasicBlock::Create(gIR->context(), "andandend", gIR->topfunc()); + + LLValue *ubool = DtoCast(e->loc, u, Type::tbool)->getRVal(); + + llvm::BasicBlock *oldblock = p->scopebb(); + llvm::BranchInst::Create(andand, andandend, ubool, p->scopebb()); + + p->scope() = IRScope(andand); + emitCoverageLinecountInc(e->e2->loc); + DValue *v = toElemDtor(e->e2); + + LLValue *vbool = 0; + if (v && !v->isFunc() && v->getType() != Type::tvoid) { + vbool = DtoCast(e->loc, v, Type::tbool)->getRVal(); } - ////////////////////////////////////////////////////////////////////////////////////////// + llvm::BasicBlock *newblock = p->scopebb(); + llvm::BranchInst::Create(andandend, p->scopebb()); + p->scope() = IRScope(andandend); - void visit(ArrayLengthExp *e) - { - IF_LOG Logger::print("ArrayLengthExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - DValue* u = toElem(e->e1); - result = new DImValue(e->type, DtoArrayLen(u)); + LLValue *resval = 0; + if (ubool == vbool || !vbool) { + // No need to create a PHI node. + resval = ubool; + } else { + llvm::PHINode *phi = + p->ir->CreatePHI(LLType::getInt1Ty(gIR->context()), 2, "andandval"); + // If we jumped over evaluation of the right-hand side, + // the result is false. Otherwise it's the value of the right-hand side. + phi->addIncoming(LLConstantInt::getFalse(gIR->context()), oldblock); + phi->addIncoming(vbool, newblock); + resval = phi; } - ////////////////////////////////////////////////////////////////////////////////////////// + result = new DImValue(e->type, resval); + } - void visit(AssertExp *e) - { - IF_LOG Logger::print("AssertExp::toElem: %s\n", e->toChars()); - LOG_SCOPE; + ////////////////////////////////////////////////////////////////////////////////////////// - // DMD allows syntax like this: - // f() == 0 || assert(false) - result = new DImValue(e->type, DtoConstBool(false)); + void visit(OrOrExp *e) { + IF_LOG Logger::print("OrOrExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - if (!global.params.useAssert) - return; + DValue *u = toElem(e->e1); - // condition - DValue* cond; - Type* condty; + llvm::BasicBlock *oror = + llvm::BasicBlock::Create(gIR->context(), "oror", gIR->topfunc()); + llvm::BasicBlock *ororend = + llvm::BasicBlock::Create(gIR->context(), "ororend", gIR->topfunc()); - // special case for dmd generated assert(this); when not in -release mode - if (e->e1->op == TOKthis && static_cast(e->e1)->var == NULL) - { - LLValue* thisarg = p->func()->thisArg; - assert(thisarg && "null thisarg, but we're in assert(this) exp;"); - LLValue* thisptr = DtoLoad(thisarg); - condty = e->e1->type->toBasetype(); - cond = new DImValue(condty, thisptr); - } - else - { - cond = toElem(e->e1); - condty = e->e1->type->toBasetype(); - } + LLValue *ubool = DtoCast(e->loc, u, Type::tbool)->getRVal(); - // create basic blocks - llvm::BasicBlock* passedbb = llvm::BasicBlock::Create(gIR->context(), "assertPassed", p->topfunc()); - llvm::BasicBlock* failedbb = llvm::BasicBlock::Create(gIR->context(), "assertFailed", p->topfunc()); + llvm::BasicBlock *oldblock = p->scopebb(); + llvm::BranchInst::Create(ororend, oror, ubool, p->scopebb()); - // test condition - LLValue* condval = DtoCast(e->loc, cond, Type::tbool)->getRVal(); + p->scope() = IRScope(oror); + emitCoverageLinecountInc(e->e2->loc); + DValue *v = toElemDtor(e->e2); - // branch - llvm::BranchInst::Create(passedbb, failedbb, condval, p->scopebb()); - - // failed: call assert runtime function - p->scope() = IRScope(failedbb); - - /* DMD Bugzilla 8360: If the condition is evaluated to true, - * 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) : NULL); - - // passed: - p->scope() = IRScope(passedbb); - - FuncDeclaration* invdecl; - // class invariants - if( - global.params.useInvariants && - condty->ty == Tclass && - !(static_cast(condty)->sym->isInterfaceDeclaration()) && - !(static_cast(condty)->sym->isCPPclass())) - { - Logger::println("calling class invariant"); - llvm::Function* fn = LLVM_D_GetRuntimeFunction(e->loc, gIR->module, - gABI->mangleForLLVM("_D9invariant12_d_invariantFC6ObjectZv", LINKd).c_str()); - LLValue* arg = DtoBitCast(cond->getRVal(), fn->getFunctionType()->getParamType(0)); - gIR->CreateCallOrInvoke(fn, arg); - } - // struct invariants - else if( - global.params.useInvariants && - condty->ty == Tpointer && condty->nextOf()->ty == Tstruct && - (invdecl = static_cast(condty->nextOf())->sym->inv) != NULL) - { - Logger::print("calling struct invariant"); - DtoResolveFunction(invdecl); - DFuncValue invfunc(invdecl, getIrFunc(invdecl)->func, cond->getRVal()); - DtoCallFunction(e->loc, NULL, &invfunc, NULL); - } + LLValue *vbool = 0; + if (v && !v->isFunc() && v->getType() != Type::tvoid) { + vbool = DtoCast(e->loc, v, Type::tbool)->getRVal(); } - ////////////////////////////////////////////////////////////////////////////////////////// + llvm::BasicBlock *newblock = p->scopebb(); + llvm::BranchInst::Create(ororend, p->scopebb()); + p->scope() = IRScope(ororend); - void visit(NotExp *e) - { - IF_LOG Logger::print("NotExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - DValue* u = toElem(e->e1); - - LLValue* b = DtoCast(e->loc, u, Type::tbool)->getRVal(); - - LLConstant* zero = DtoConstBool(false); - b = p->ir->CreateICmpEQ(b,zero); - - result = new DImValue(e->type, b); + LLValue *resval = 0; + if (ubool == vbool || !vbool) { + // No need to create a PHI node. + resval = ubool; + } else { + llvm::PHINode *phi = + p->ir->CreatePHI(LLType::getInt1Ty(gIR->context()), 2, "ororval"); + // If we jumped over evaluation of the right-hand side, + // the result is true. Otherwise, it's the value of the right-hand side. + phi->addIncoming(LLConstantInt::getTrue(gIR->context()), oldblock); + phi->addIncoming(vbool, newblock); + resval = phi; } - ////////////////////////////////////////////////////////////////////////////////////////// + result = new DImValue(e->type, resval); + } - void visit(AndAndExp *e) - { - IF_LOG Logger::print("AndAndExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; +////////////////////////////////////////////////////////////////////////////////////////// - DValue* u = toElem(e->e1); +#define BinBitExp(X, Y) \ + void visit(X##Exp *e) { \ + IF_LOG Logger::print("%sExp::toElem: %s @ %s\n", #X, e->toChars(), \ + e->type->toChars()); \ + LOG_SCOPE; \ + DValue *u = toElem(e->e1); \ + DValue *v = toElem(e->e2); \ + errorOnIllegalArrayOp(e, e->e1, e->e2); \ + v = DtoCast(e->loc, v, e->e1->type); \ + LLValue *x = llvm::BinaryOperator::Create( \ + llvm::Instruction::Y, u->getRVal(), v->getRVal(), "", p->scopebb()); \ + result = new DImValue(e->type, x); \ + } - llvm::BasicBlock* andand = llvm::BasicBlock::Create(gIR->context(), "andand", gIR->topfunc()); - llvm::BasicBlock* andandend = llvm::BasicBlock::Create(gIR->context(), "andandend", gIR->topfunc()); + BinBitExp(And, And) BinBitExp(Or, Or) BinBitExp(Xor, Xor) BinBitExp(Shl, Shl) + BinBitExp(Ushr, LShr) - LLValue* ubool = DtoCast(e->loc, u, Type::tbool)->getRVal(); + void visit(ShrExp *e) { + IF_LOG Logger::print("ShrExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + DValue *u = toElem(e->e1); + DValue *v = toElem(e->e2); + v = DtoCast(e->loc, v, e->e1->type); + LLValue *x; + if (isLLVMUnsigned(e->e1->type)) + x = p->ir->CreateLShr(u->getRVal(), v->getRVal()); + else + x = p->ir->CreateAShr(u->getRVal(), v->getRVal()); + result = new DImValue(e->type, x); + } - llvm::BasicBlock* oldblock = p->scopebb(); - llvm::BranchInst::Create(andand, andandend, ubool, p->scopebb()); + ////////////////////////////////////////////////////////////////////////////////////////// - p->scope() = IRScope(andand); - emitCoverageLinecountInc(e->e2->loc); - DValue* v = toElemDtor(e->e2); - - LLValue* vbool = 0; - if (v && !v->isFunc() && v->getType() != Type::tvoid) - { - vbool = DtoCast(e->loc, v, Type::tbool)->getRVal(); - } - - llvm::BasicBlock* newblock = p->scopebb(); - llvm::BranchInst::Create(andandend,p->scopebb()); - p->scope() = IRScope(andandend); - - LLValue* resval = 0; - if (ubool == vbool || !vbool) { - // No need to create a PHI node. - resval = ubool; - } else { - llvm::PHINode* phi = p->ir->CreatePHI(LLType::getInt1Ty(gIR->context()), 2, "andandval"); - // If we jumped over evaluation of the right-hand side, - // the result is false. Otherwise it's the value of the right-hand side. - phi->addIncoming(LLConstantInt::getFalse(gIR->context()), oldblock); - phi->addIncoming(vbool, newblock); - resval = phi; - } - - result = new DImValue(e->type, resval); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(OrOrExp *e) - { - IF_LOG Logger::print("OrOrExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - DValue* u = toElem(e->e1); - - llvm::BasicBlock* oror = llvm::BasicBlock::Create(gIR->context(), "oror", gIR->topfunc()); - llvm::BasicBlock* ororend = llvm::BasicBlock::Create(gIR->context(), "ororend", gIR->topfunc()); - - LLValue* ubool = DtoCast(e->loc, u, Type::tbool)->getRVal(); - - llvm::BasicBlock* oldblock = p->scopebb(); - llvm::BranchInst::Create(ororend,oror,ubool,p->scopebb()); - - p->scope() = IRScope(oror); - emitCoverageLinecountInc(e->e2->loc); - DValue* v = toElemDtor(e->e2); - - LLValue* vbool = 0; - if (v && !v->isFunc() && v->getType() != Type::tvoid) - { - vbool = DtoCast(e->loc, v, Type::tbool)->getRVal(); - } - - llvm::BasicBlock* newblock = p->scopebb(); - llvm::BranchInst::Create(ororend,p->scopebb()); - p->scope() = IRScope(ororend); - - LLValue* resval = 0; - if (ubool == vbool || !vbool) { - // No need to create a PHI node. - resval = ubool; - } else { - llvm::PHINode* phi = p->ir->CreatePHI(LLType::getInt1Ty(gIR->context()), 2, "ororval"); - // If we jumped over evaluation of the right-hand side, - // the result is true. Otherwise, it's the value of the right-hand side. - phi->addIncoming(LLConstantInt::getTrue(gIR->context()), oldblock); - phi->addIncoming(vbool, newblock); - resval = phi; - } - - result = new DImValue(e->type, resval); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - #define BinBitExp(X,Y) \ - void visit(X##Exp *e) \ - { \ - IF_LOG Logger::print("%sExp::toElem: %s @ %s\n", #X, e->toChars(), e->type->toChars()); \ - LOG_SCOPE; \ - DValue* u = toElem(e->e1); \ - DValue* v = toElem(e->e2); \ - errorOnIllegalArrayOp(e, e->e1, e->e2); \ - v = DtoCast(e->loc, v, e->e1->type); \ - LLValue* x = llvm::BinaryOperator::Create(llvm::Instruction::Y, u->getRVal(), v->getRVal(), "", p->scopebb()); \ - result = new DImValue(e->type, x); \ - } - - BinBitExp(And,And) - BinBitExp(Or,Or) - BinBitExp(Xor,Xor) - BinBitExp(Shl,Shl) - BinBitExp(Ushr,LShr) - - void visit(ShrExp *e) - { - IF_LOG Logger::print("ShrExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - DValue* u = toElem(e->e1); - DValue* v = toElem(e->e2); - v = DtoCast(e->loc, v, e->e1->type); - LLValue* x; - if (isLLVMUnsigned(e->e1->type)) - x = p->ir->CreateLShr(u->getRVal(), v->getRVal()); - else - x = p->ir->CreateAShr(u->getRVal(), v->getRVal()); - result = new DImValue(e->type, x); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(HaltExp *e) - { - IF_LOG Logger::print("HaltExp::toElem: %s\n", e->toChars()); - LOG_SCOPE; + void visit(HaltExp *e) { + IF_LOG Logger::print("HaltExp::toElem: %s\n", e->toChars()); + LOG_SCOPE; #if LDC_LLVM_VER >= 307 - p->ir->CreateCall(GET_INTRINSIC_DECL(trap), {}); + p->ir->CreateCall(GET_INTRINSIC_DECL(trap), {}); #else - p->ir->CreateCall(GET_INTRINSIC_DECL(trap), ""); + p->ir->CreateCall(GET_INTRINSIC_DECL(trap), ""); #endif - p->ir->CreateUnreachable(); + p->ir->CreateUnreachable(); - // this terminated the basicblock, start a new one - // this is sensible, since someone might goto behind the assert - // and prevents compiler errors if a terminator follows the assert - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterhalt", p->topfunc()); - p->scope() = IRScope(bb); + // this terminated the basicblock, start a new one + // this is sensible, since someone might goto behind the assert + // and prevents compiler errors if a terminator follows the assert + llvm::BasicBlock *bb = + llvm::BasicBlock::Create(gIR->context(), "afterhalt", p->topfunc()); + p->scope() = IRScope(bb); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(DelegateExp *e) { + IF_LOG Logger::print("DelegateExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + if (e->func->isStatic()) + e->error("can't take delegate of static function %s, it does not require " + "a context ptr", + e->func->toChars()); + + LLPointerType *int8ptrty = getPtrToType(LLType::getInt8Ty(gIR->context())); + + assert(e->type->toBasetype()->ty == Tdelegate); + LLType *dgty = DtoType(e->type); + + DValue *u = toElem(e->e1); + LLValue *uval; + if (DFuncValue *f = u->isFunc()) { + assert(f->func); + LLValue *contextptr = DtoNestedContext(e->loc, f->func); + uval = DtoBitCast(contextptr, getVoidPtrType()); + } else { + uval = u->getRVal(); } - ////////////////////////////////////////////////////////////////////////////////////////// + IF_LOG Logger::cout() << "context = " << *uval << '\n'; - void visit(DelegateExp *e) + LLValue *castcontext = DtoBitCast(uval, int8ptrty); + + IF_LOG Logger::println("func: '%s'", e->func->toPrettyChars()); + + LLValue *castfptr; + + if (e->e1->op != TOKsuper && e->e1->op != TOKdottype && + e->func->isVirtual() && !e->func->isFinalFunc()) + castfptr = DtoVirtualFunctionPointer(u, e->func, e->toChars()); + else if (e->func->isAbstract()) + llvm_unreachable("Delegate to abstract method not implemented."); + else if (e->func->toParent()->isInterfaceDeclaration()) + llvm_unreachable("Delegate to interface method not implemented."); + else { + DtoResolveFunction(e->func); + + // We need to actually codegen the function here, as literals are not + // added to the module member list. + if (e->func->semanticRun == PASSsemantic3done) { + Dsymbol *owner = e->func->toParent(); + while (!owner->isTemplateInstance() && owner->toParent()) + owner = owner->toParent(); + if (owner->isTemplateInstance() || owner == p->dmodule) { + Declaration_codegen(e->func, p); + } + } + + castfptr = getIrFunc(e->func)->func; + } + + castfptr = DtoBitCast(castfptr, dgty->getContainedType(1)); + + result = new DImValue( + e->type, DtoAggrPair(DtoType(e->type), castcontext, castfptr, ".dg")); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(IdentityExp *e) { + IF_LOG Logger::print("IdentityExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *l = toElem(e->e1); + DValue *r = toElem(e->e2); + LLValue *lv = l->getRVal(); + LLValue *rv = r->getRVal(); + + Type *t1 = e->e1->type->toBasetype(); + + // handle dynarray specially + if (t1->ty == Tarray) { + result = new DImValue(e->type, DtoDynArrayIs(e->op, l, r)); + return; + } + // also structs + else if (t1->ty == Tstruct) { + result = new DImValue(e->type, DtoStructEquals(e->op, l, r)); + return; + } + + // FIXME this stuff isn't pretty + LLValue *eval = 0; + + if (t1->ty == Tdelegate) { + if (r->isNull()) { + rv = NULL; + } else { + assert(lv->getType() == rv->getType()); + } + eval = DtoDelegateEquals(e->op, lv, rv); + } else if (t1->isfloating()) // includes iscomplex { - IF_LOG Logger::print("DelegateExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - if(e->func->isStatic()) - e->error("can't take delegate of static function %s, it does not require a context ptr", e->func->toChars()); - - LLPointerType* int8ptrty = getPtrToType(LLType::getInt8Ty(gIR->context())); - - assert(e->type->toBasetype()->ty == Tdelegate); - LLType* dgty = DtoType(e->type); - - DValue* u = toElem(e->e1); - LLValue* uval; - if (DFuncValue* f = u->isFunc()) { - assert(f->func); - LLValue* contextptr = DtoNestedContext(e->loc, f->func); - uval = DtoBitCast(contextptr, getVoidPtrType()); - } - else { - uval = u->getRVal(); - } - - IF_LOG Logger::cout() << "context = " << *uval << '\n'; - - LLValue* castcontext = DtoBitCast(uval, int8ptrty); - - IF_LOG Logger::println("func: '%s'", e->func->toPrettyChars()); - - LLValue* castfptr; - - if (e->e1->op != TOKsuper && e->e1->op != TOKdottype && e->func->isVirtual() && !e->func->isFinalFunc()) - castfptr = DtoVirtualFunctionPointer(u, e->func, e->toChars()); - else if (e->func->isAbstract()) - llvm_unreachable("Delegate to abstract method not implemented."); - else if (e->func->toParent()->isInterfaceDeclaration()) - llvm_unreachable("Delegate to interface method not implemented."); + eval = DtoBinNumericEquals(e->loc, l, r, e->op); + } else if (t1->ty == Tpointer || t1->ty == Tclass) { + if (lv->getType() != rv->getType()) { + if (r->isNull()) + rv = llvm::ConstantPointerNull::get(isaPointer(lv->getType())); else - { - DtoResolveFunction(e->func); + rv = DtoBitCast(rv, lv->getType()); + } + eval = (e->op == TOKidentity) ? p->ir->CreateICmpEQ(lv, rv) + : p->ir->CreateICmpNE(lv, rv); + } else { + assert(lv->getType() == rv->getType()); + eval = (e->op == TOKidentity) ? p->ir->CreateICmpEQ(lv, rv) + : p->ir->CreateICmpNE(lv, rv); + } + result = new DImValue(e->type, eval); + } - // We need to actually codegen the function here, as literals are not - // added to the module member list. - if (e->func->semanticRun == PASSsemantic3done) - { - Dsymbol *owner = e->func->toParent(); - while (!owner->isTemplateInstance() && owner->toParent()) - owner = owner->toParent(); - if (owner->isTemplateInstance() || owner == p->dmodule) - { - Declaration_codegen(e->func, p); - } - } + ////////////////////////////////////////////////////////////////////////////////////////// - castfptr = getIrFunc(e->func)->func; - } + void visit(CommaExp *e) { + IF_LOG Logger::print("CommaExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - castfptr = DtoBitCast(castfptr, dgty->getContainedType(1)); - - result = new DImValue(e->type, DtoAggrPair(DtoType(e->type), castcontext, castfptr, ".dg")); + if (e->cachedLvalue) { + LLValue *V = e->cachedLvalue; + result = new DVarValue(e->type, V); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + toElem(e->e1); + result = toElem(e->e2); - void visit(IdentityExp *e) - { - IF_LOG Logger::print("IdentityExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + // Actually, we can get qualifier mismatches in the 2.064 frontend: + // assert(e2->type == type); + } - DValue* l = toElem(e->e1); - DValue* r = toElem(e->e2); - LLValue* lv = l->getRVal(); - LLValue* rv = r->getRVal(); + ////////////////////////////////////////////////////////////////////////////////////////// - Type* t1 = e->e1->type->toBasetype(); + void visit(CondExp *e) { + IF_LOG Logger::print("CondExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // handle dynarray specially - if (t1->ty == Tarray) { - result = new DImValue(e->type, DtoDynArrayIs(e->op, l, r)); - return; - } - // also structs - else if (t1->ty == Tstruct) { - result = new DImValue(e->type, DtoStructEquals(e->op, l, r)); - return; - } - - // FIXME this stuff isn't pretty - LLValue* eval = 0; - - if (t1->ty == Tdelegate) { - if (r->isNull()) { - rv = NULL; - } - else { - assert(lv->getType() == rv->getType()); - } - eval = DtoDelegateEquals(e->op,lv,rv); - } - else if (t1->isfloating()) // includes iscomplex - { - eval = DtoBinNumericEquals(e->loc, l, r, e->op); - } - else if (t1->ty == Tpointer || t1->ty == Tclass) - { - if (lv->getType() != rv->getType()) { - if (r->isNull()) - rv = llvm::ConstantPointerNull::get(isaPointer(lv->getType())); - else - rv = DtoBitCast(rv, lv->getType()); - } - eval = (e->op == TOKidentity) - ? p->ir->CreateICmpEQ(lv, rv) - : p->ir->CreateICmpNE(lv, rv); - } - else { - assert(lv->getType() == rv->getType()); - eval = (e->op == TOKidentity) - ? p->ir->CreateICmpEQ(lv, rv) - : p->ir->CreateICmpNE(lv, rv); - } - result = new DImValue(e->type, eval); + Type *dtype = e->type->toBasetype(); + LLValue *retPtr = 0; + if (dtype->ty != Tvoid) { + // allocate a temporary for pointer to the final result. + retPtr = DtoAlloca(dtype->pointerTo(), "condtmp"); } - ////////////////////////////////////////////////////////////////////////////////////////// + llvm::BasicBlock *condtrue = + llvm::BasicBlock::Create(gIR->context(), "condtrue", gIR->topfunc()); + llvm::BasicBlock *condfalse = + llvm::BasicBlock::Create(gIR->context(), "condfalse", gIR->topfunc()); + llvm::BasicBlock *condend = + llvm::BasicBlock::Create(gIR->context(), "condend", gIR->topfunc()); - void visit(CommaExp *e) - { - IF_LOG Logger::print("CommaExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + DValue *c = toElem(e->econd); + LLValue *cond_val = DtoCast(e->loc, c, Type::tbool)->getRVal(); + llvm::BranchInst::Create(condtrue, condfalse, cond_val, p->scopebb()); - if (e->cachedLvalue) - { - LLValue* V = e->cachedLvalue; - result = new DVarValue(e->type, V); - return; - } + p->scope() = IRScope(condtrue); + DValue *u = toElem(e->e1); + if (retPtr) { + LLValue *lval = makeLValue(e->loc, u); + DtoStore(lval, DtoBitCast(retPtr, lval->getType()->getPointerTo())); + } + llvm::BranchInst::Create(condend, p->scopebb()); - toElem(e->e1); - result = toElem(e->e2); + p->scope() = IRScope(condfalse); + DValue *v = toElem(e->e2); + if (retPtr) { + LLValue *lval = makeLValue(e->loc, v); + DtoStore(lval, DtoBitCast(retPtr, lval->getType()->getPointerTo())); + } + llvm::BranchInst::Create(condend, p->scopebb()); - // Actually, we can get qualifier mismatches in the 2.064 frontend: - // assert(e2->type == type); + p->scope() = IRScope(condend); + if (retPtr) + result = new DVarValue(e->type, DtoLoad(retPtr)); + else + result = new DConstValue(e->type, getNullValue(DtoMemType(dtype))); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(ComExp *e) { + IF_LOG Logger::print("ComExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *u = toElem(e->e1); + + LLValue *value = u->getRVal(); + LLValue *minusone = + LLConstantInt::get(value->getType(), static_cast(-1), true); + value = llvm::BinaryOperator::Create(llvm::Instruction::Xor, value, + minusone, "", p->scopebb()); + + result = new DImValue(e->type, value); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(NegExp *e) { + IF_LOG Logger::print("NegExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *l = toElem(e->e1); + + if (e->type->iscomplex()) { + result = DtoComplexNeg(e->loc, e->type, l); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + LLValue *val = l->getRVal(); - void visit(CondExp *e) - { - IF_LOG Logger::print("CondExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + if (e->type->isintegral()) + val = p->ir->CreateNeg(val, "negval"); + else + val = p->ir->CreateFNeg(val, "negval"); - Type* dtype = e->type->toBasetype(); - LLValue* retPtr = 0; - if (dtype->ty != Tvoid) { - // allocate a temporary for pointer to the final result. - retPtr = DtoAlloca(dtype->pointerTo(), "condtmp"); - } + result = new DImValue(e->type, val); + } - llvm::BasicBlock* condtrue = llvm::BasicBlock::Create(gIR->context(), "condtrue", gIR->topfunc()); - llvm::BasicBlock* condfalse = llvm::BasicBlock::Create(gIR->context(), "condfalse", gIR->topfunc()); - llvm::BasicBlock* condend = llvm::BasicBlock::Create(gIR->context(), "condend", gIR->topfunc()); + ////////////////////////////////////////////////////////////////////////////////////////// - DValue* c = toElem(e->econd); - LLValue* cond_val = DtoCast(e->loc, c, Type::tbool)->getRVal(); - llvm::BranchInst::Create(condtrue, condfalse, cond_val, p->scopebb()); + void visit(CatExp *e) { + IF_LOG Logger::print("CatExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - p->scope() = IRScope(condtrue); - DValue* u = toElem(e->e1); - if (retPtr) { - LLValue* lval = makeLValue(e->loc, u); - DtoStore(lval, DtoBitCast(retPtr, lval->getType()->getPointerTo())); - } - llvm::BranchInst::Create(condend, p->scopebb()); + result = DtoCatArrays(e->loc, e->type, e->e1, e->e2); + } - p->scope() = IRScope(condfalse); - DValue* v = toElem(e->e2); - if (retPtr) { - LLValue* lval = makeLValue(e->loc, v); - DtoStore(lval, DtoBitCast(retPtr, lval->getType()->getPointerTo())); - } - llvm::BranchInst::Create(condend, p->scopebb()); + ////////////////////////////////////////////////////////////////////////////////////////// - p->scope() = IRScope(condend); - if (retPtr) - result = new DVarValue(e->type, DtoLoad(retPtr)); - else - result = new DConstValue(e->type, getNullValue(DtoMemType(dtype))); + void visit(CatAssignExp *e) { + IF_LOG Logger::print("CatAssignExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + result = toElem(e->e1); + + Type *e1type = e->e1->type->toBasetype(); + assert(e1type->ty == Tarray); + Type *elemtype = e1type->nextOf()->toBasetype(); + Type *e2type = e->e2->type->toBasetype(); + + if (e1type->ty == Tarray && e2type->ty == Tdchar && + (elemtype->ty == Tchar || elemtype->ty == Twchar)) { + if (elemtype->ty == Tchar) + // append dchar to char[] + DtoAppendDCharToString(e->loc, result, e->e2); + else /*if (elemtype->ty == Twchar)*/ + // append dchar to wchar[] + DtoAppendDCharToUnicodeString(e->loc, result, e->e2); + } else if (e1type->equals(e2type)) { + // append array + DSliceValue *slice = DtoCatAssignArray(e->loc, result, e->e2); + DtoAssign(e->loc, result, slice); + } else { + // append element + DtoCatAssignElement(e->loc, e1type, result, e->e2); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(FuncExp *e) { + IF_LOG Logger::print("FuncExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + FuncLiteralDeclaration *fd = e->fd; + assert(fd); + + if ((fd->tok == TOKreserved || fd->tok == TOKdelegate) && + e->type->ty == Tpointer) { + // This is a lambda that was inferred to be a function literal instead + // of a delegate, so set tok here in order to get correct types/mangling. + // Horrible hack, but DMD does the same thing. + fd->tok = TOKfunction; + fd->vthis = NULL; } - ////////////////////////////////////////////////////////////////////////////////////////// + if (fd->isNested()) + Logger::println("nested"); + Logger::println("kind = %s", fd->kind()); - void visit(ComExp *e) - { - IF_LOG Logger::print("ComExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - DValue* u = toElem(e->e1); - - LLValue* value = u->getRVal(); - LLValue* minusone = LLConstantInt::get(value->getType(), static_cast(-1), true); - value = llvm::BinaryOperator::Create(llvm::Instruction::Xor, value, minusone, "", p->scopebb()); - - result = new DImValue(e->type, value); + // We need to actually codegen the function here, as literals are not added + // to the module member list. + Declaration_codegen(fd, p); + if (!isIrFuncCreated(fd)) { + // See DtoDefineFunction for reasons why codegen was suppressed. + // Instead just declare the function. + DtoDeclareFunction(fd); + assert(!fd->isNested()); } + assert(getIrFunc(fd)->func); - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(NegExp *e) - { - IF_LOG Logger::print("NegExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - DValue* l = toElem(e->e1); - - if (e->type->iscomplex()) { - result = DtoComplexNeg(e->loc, e->type, l); - return; - } - - LLValue* val = l->getRVal(); - - if (e->type->isintegral()) - val = p->ir->CreateNeg(val,"negval"); - else - val = p->ir->CreateFNeg(val,"negval"); - - result = new DImValue(e->type, val); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(CatExp *e) - { - IF_LOG Logger::print("CatExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - result = DtoCatArrays(e->loc, e->type, e->e1, e->e2); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(CatAssignExp *e) - { - IF_LOG Logger::print("CatAssignExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - result = toElem(e->e1); - - Type* e1type = e->e1->type->toBasetype(); - assert(e1type->ty == Tarray); - Type* elemtype = e1type->nextOf()->toBasetype(); - Type* e2type = e->e2->type->toBasetype(); - - if (e1type->ty == Tarray && e2type->ty == Tdchar && - (elemtype->ty == Tchar || elemtype->ty == Twchar)) - { - if (elemtype->ty == Tchar) - // append dchar to char[] - DtoAppendDCharToString(e->loc, result, e->e2); - else /*if (elemtype->ty == Twchar)*/ - // append dchar to wchar[] - DtoAppendDCharToUnicodeString(e->loc, result, e->e2); - } - else if (e1type->equals(e2type)) { - // append array - DSliceValue* slice = DtoCatAssignArray(e->loc, result, e->e2); - DtoAssign(e->loc, result, slice); - } - else { - // append element - DtoCatAssignElement(e->loc, e1type, result, e->e2); - } - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(FuncExp *e) - { - IF_LOG Logger::print("FuncExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - FuncLiteralDeclaration *fd = e->fd; - assert(fd); - - if ((fd->tok == TOKreserved || fd->tok == TOKdelegate) && e->type->ty == Tpointer) - { - // This is a lambda that was inferred to be a function literal instead - // of a delegate, so set tok here in order to get correct types/mangling. - // Horrible hack, but DMD does the same thing. - fd->tok = TOKfunction; - fd->vthis = NULL; - } - - if (fd->isNested()) Logger::println("nested"); - Logger::println("kind = %s", fd->kind()); - - // We need to actually codegen the function here, as literals are not added - // to the module member list. - Declaration_codegen(fd, p); - if (!isIrFuncCreated(fd)) - { - // See DtoDefineFunction for reasons why codegen was suppressed. - // Instead just declare the function. - DtoDeclareFunction(fd); - assert(!fd->isNested()); - } - assert(getIrFunc(fd)->func); - - if (fd->isNested()) { - LLType* dgty = DtoType(e->type); - - LLValue* cval; - IrFunction* irfn = p->func(); - if (irfn->nestedVar && fd->toParent2() == irfn->decl) - { - // We check fd->toParent2() because a frame allocated in one - // function cannot be used for a delegate created in another - // function. Happens with anonymous functions. - cval = irfn->nestedVar; - } - else if (irfn->nestArg) - { - cval = DtoLoad(irfn->nestArg); - } - else if (irfn->thisArg) - { - AggregateDeclaration* ad = irfn->decl->isMember2(); - if (!ad || !ad->vthis) - { - cval = getNullPtr(getVoidPtrType()); - } - else - { - cval = ad->isClassDeclaration() ? DtoLoad(irfn->thisArg) : irfn->thisArg; - cval = DtoLoad(DtoGEPi(cval, 0, getFieldGEPIndex(ad, ad->vthis), ".vthis")); - } - } - else - { - cval = getNullPtr(getVoidPtrType()); - } - cval = DtoBitCast(cval, dgty->getContainedType(0)); - - LLValue* castfptr = DtoBitCast(getIrFunc(fd)->func, dgty->getContainedType(1)); - - result = new DImValue(e->type, DtoAggrPair(cval, castfptr, ".func")); + if (fd->isNested()) { + LLType *dgty = DtoType(e->type); + LLValue *cval; + IrFunction *irfn = p->func(); + if (irfn->nestedVar && fd->toParent2() == irfn->decl) { + // We check fd->toParent2() because a frame allocated in one + // function cannot be used for a delegate created in another + // function. Happens with anonymous functions. + cval = irfn->nestedVar; + } else if (irfn->nestArg) { + cval = DtoLoad(irfn->nestArg); + } else if (irfn->thisArg) { + AggregateDeclaration *ad = irfn->decl->isMember2(); + if (!ad || !ad->vthis) { + cval = getNullPtr(getVoidPtrType()); } else { - result = new DFuncValue(e->type, fd, getIrFunc(fd)->func); + cval = + ad->isClassDeclaration() ? DtoLoad(irfn->thisArg) : irfn->thisArg; + cval = DtoLoad( + DtoGEPi(cval, 0, getFieldGEPIndex(ad, ad->vthis), ".vthis")); } + } else { + cval = getNullPtr(getVoidPtrType()); + } + cval = DtoBitCast(cval, dgty->getContainedType(0)); + + LLValue *castfptr = + DtoBitCast(getIrFunc(fd)->func, dgty->getContainedType(1)); + + result = new DImValue(e->type, DtoAggrPair(cval, castfptr, ".func")); + + } else { + result = new DFuncValue(e->type, fd, getIrFunc(fd)->func); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(ArrayLiteralExp *e) { + IF_LOG Logger::print("ArrayLiteralExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + // D types + Type *arrayType = e->type->toBasetype(); + Type *elemType = arrayType->nextOf()->toBasetype(); + + // is dynamic ? + bool const dyn = (arrayType->ty == Tarray); + // length + size_t const len = e->elements->dim; + + // llvm target type + LLType *llType = DtoType(arrayType); + IF_LOG Logger::cout() << (dyn ? "dynamic" : "static") + << " array literal with length " << len + << " of D type: '" << arrayType->toChars() + << "' has llvm type: '" << *llType << "'\n"; + + // llvm storage type + LLType *llElemType = DtoMemType(elemType); + LLType *llStoType = LLArrayType::get(llElemType, len); + IF_LOG Logger::cout() << "llvm storage type: '" << *llStoType << "'\n"; + + // don't allocate storage for zero length dynamic array literals + if (dyn && len == 0) { + // dmd seems to just make them null... + result = new DSliceValue(e->type, DtoConstSize_t(0), + getNullPtr(getPtrToType(llElemType))); + } else if (dyn) { + if (arrayType->isImmutable() && isConstLiteral(e)) { + llvm::Constant *init = arrayLiteralToConst(p, e); + llvm::GlobalVariable *global = new llvm::GlobalVariable( + gIR->module, init->getType(), true, + llvm::GlobalValue::InternalLinkage, init, ".immutablearray"); + result = new DSliceValue(arrayType, DtoConstSize_t(e->elements->dim), + DtoBitCast(global, getPtrToType(llElemType))); + } else { + DSliceValue *dynSlice = DtoNewDynArray( + e->loc, arrayType, + new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false); + initializeArrayLiteral( + p, e, DtoBitCast(dynSlice->ptr, getPtrToType(llStoType))); + result = dynSlice; + } + } else { + llvm::Value *storage = + DtoRawAlloca(llStoType, DtoAlignment(e->type), "arrayliteral"); + initializeArrayLiteral(p, e, storage); + result = new DImValue(e->type, storage); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(StructLiteralExp *e) { + IF_LOG Logger::print("StructLiteralExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + if (e->sinit) { + // Copied from VarExp::toElem, need to clean this mess up. + Type *sdecltype = e->sinit->type->toBasetype(); + IF_LOG Logger::print("Sym: type = %s\n", sdecltype->toChars()); + assert(sdecltype->ty == Tstruct); + TypeStruct *ts = static_cast(sdecltype); + assert(ts->sym); + DtoResolveStruct(ts->sym); + + LLValue *initsym = getIrAggr(ts->sym)->getInitSymbol(); + initsym = DtoBitCast(initsym, DtoType(ts->pointerTo())); + result = new DVarValue(e->type, initsym); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(ArrayLiteralExp *e) - { - IF_LOG Logger::print("ArrayLiteralExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - // D types - Type* arrayType = e->type->toBasetype(); - Type* elemType = arrayType->nextOf()->toBasetype(); - - // is dynamic ? - bool const dyn = (arrayType->ty == Tarray); - // length - size_t const len = e->elements->dim; - - // llvm target type - LLType* llType = DtoType(arrayType); - IF_LOG Logger::cout() << (dyn?"dynamic":"static") << " array literal with length " << len << " of D type: '" << arrayType->toChars() << "' has llvm type: '" << *llType << "'\n"; - - // llvm storage type - LLType* llElemType = DtoMemType(elemType); - LLType* llStoType = LLArrayType::get(llElemType, len); - IF_LOG Logger::cout() << "llvm storage type: '" << *llStoType << "'\n"; - - // don't allocate storage for zero length dynamic array literals - if (dyn && len == 0) - { - // dmd seems to just make them null... - result = new DSliceValue(e->type, DtoConstSize_t(0), getNullPtr(getPtrToType(llElemType))); - } - else if (dyn) - { - if (arrayType->isImmutable() && isConstLiteral(e)) - { - llvm::Constant* init = arrayLiteralToConst(p, e); - llvm::GlobalVariable* global = new llvm::GlobalVariable( - gIR->module, - init->getType(), - true, - llvm::GlobalValue::InternalLinkage, - init, - ".immutablearray" - ); - result = new DSliceValue(arrayType, DtoConstSize_t(e->elements->dim), - DtoBitCast(global, getPtrToType(llElemType))); - } - else - { - DSliceValue* dynSlice = DtoNewDynArray(e->loc, arrayType, - new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false); - initializeArrayLiteral(p, e, DtoBitCast(dynSlice->ptr, getPtrToType(llStoType))); - result = dynSlice; - } - } - else - { - llvm::Value* storage = DtoRawAlloca(llStoType, DtoAlignment(e->type), "arrayliteral"); - initializeArrayLiteral(p, e, storage); - result = new DImValue(e->type, storage); - } + if (e->inProgressMemory) { + result = new DVarValue(e->type, e->inProgressMemory); + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + // make sure the struct is fully resolved + DtoResolveStruct(e->sd); - void visit(StructLiteralExp *e) - { - IF_LOG Logger::print("StructLiteralExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + // alloca a stack slot + e->inProgressMemory = DtoAlloca(e->type, ".structliteral"); - if (e->sinit) - { - // Copied from VarExp::toElem, need to clean this mess up. - Type* sdecltype = e->sinit->type->toBasetype(); - IF_LOG Logger::print("Sym: type = %s\n", sdecltype->toChars()); - assert(sdecltype->ty == Tstruct); - TypeStruct* ts = static_cast(sdecltype); - assert(ts->sym); - DtoResolveStruct(ts->sym); + // fill the allocated struct literal + write_struct_literal(e->loc, e->inProgressMemory, e->sd, e->elements); - LLValue* initsym = getIrAggr(ts->sym)->getInitSymbol(); - initsym = DtoBitCast(initsym, DtoType(ts->pointerTo())); - result = new DVarValue(e->type, initsym); - return; - } + // return as a var + result = new DVarValue(e->type, e->inProgressMemory); + e->inProgressMemory = 0; + } - if (e->inProgressMemory) - { - result = new DVarValue(e->type, e->inProgressMemory); - return; - } + ////////////////////////////////////////////////////////////////////////////////////////// - // make sure the struct is fully resolved - DtoResolveStruct(e->sd); + void visit(ClassReferenceExp *e) { + IF_LOG Logger::print("ClassReferenceExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; - // alloca a stack slot - e->inProgressMemory = DtoAlloca(e->type, ".structliteral"); + result = new DImValue(e->type, toConstElem(e, p)); + } - // fill the allocated struct literal - write_struct_literal(e->loc, e->inProgressMemory, e->sd, e->elements); + ////////////////////////////////////////////////////////////////////////////////////////// - // return as a var - result = new DVarValue(e->type, e->inProgressMemory); - e->inProgressMemory = 0; + void visit(InExp *e) { + IF_LOG Logger::print("InExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + DValue *key = toElem(e->e1); + DValue *aa = toElem(e->e2); + + result = DtoAAIn(e->loc, e->type, aa, key); + } + + void visit(RemoveExp *e) { + IF_LOG Logger::print("RemoveExp::toElem: %s\n", e->toChars()); + LOG_SCOPE; + + DValue *aa = toElem(e->e1); + DValue *key = toElem(e->e2); + + result = DtoAARemove(e->loc, aa, key); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + /// Constructs an array initializer constant with the given constants as its + /// elements. If the element types differ (unions, …), an anonymous struct + /// literal is emitted (as for array constant initializers). + llvm::Constant *arrayConst(std::vector &vals, + Type *nominalElemType) { + if (vals.size() == 0) { + llvm::ArrayType *type = llvm::ArrayType::get(DtoType(nominalElemType), 0); + return llvm::ConstantArray::get(type, vals); } - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(ClassReferenceExp *e) - { - IF_LOG Logger::print("ClassReferenceExp::toElem: %s @ %s\n", - e->toChars(), e->type->toChars()); - LOG_SCOPE; - - result = new DImValue(e->type, toConstElem(e, p)); + llvm::Type *elementType = NULL; + bool differentTypes = false; + for (auto v : vals) { + if (!elementType) + elementType = v->getType(); + else + differentTypes |= (elementType != v->getType()); } - ////////////////////////////////////////////////////////////////////////////////////////// + if (differentTypes) + return llvm::ConstantStruct::getAnon(vals, true); - void visit(InExp *e) - { - IF_LOG Logger::print("InExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + llvm::ArrayType *t = llvm::ArrayType::get(elementType, vals.size()); + return llvm::ConstantArray::get(t, vals); + } - DValue* key = toElem(e->e1); - DValue* aa = toElem(e->e2); + void visit(AssocArrayLiteralExp *e) { + IF_LOG Logger::print("AssocArrayLiteralExp::toElem: %s @ %s\n", + e->toChars(), e->type->toChars()); + LOG_SCOPE; - result = DtoAAIn(e->loc, e->type, aa, key); + assert(e->keys); + assert(e->values); + assert(e->keys->dim == e->values->dim); + + Type *basetype = e->type->toBasetype(); + Type *aatype = basetype; + Type *vtype = aatype->nextOf(); + + if (!e->keys->dim) + goto LruntimeInit; + + if (aatype->ty != Taarray) { + // It's the AssociativeArray type. + // Turn it back into a TypeAArray + vtype = e->values->tdata()[0]->type; + aatype = new TypeAArray(vtype, e->keys->tdata()[0]->type); + aatype = aatype->semantic(e->loc, NULL); } - void visit(RemoveExp *e) { - IF_LOG Logger::print("RemoveExp::toElem: %s\n", e->toChars()); - LOG_SCOPE; + std::vector keysInits, valuesInits; + keysInits.reserve(e->keys->dim); + valuesInits.reserve(e->keys->dim); + for (size_t i = 0, n = e->keys->dim; i < n; ++i) { + Expression *ekey = e->keys->tdata()[i]; + Expression *eval = e->values->tdata()[i]; + IF_LOG Logger::println("(%llu) aa[%s] = %s", + static_cast(i), + ekey->toChars(), eval->toChars()); + unsigned errors = global.startGagging(); + LLConstant *ekeyConst = toConstElem(ekey, p); + LLConstant *evalConst = toConstElem(eval, p); + if (global.endGagging(errors)) + goto LruntimeInit; + assert(ekeyConst && evalConst); + keysInits.push_back(ekeyConst); + valuesInits.push_back(evalConst); + } - DValue* aa = toElem(e->e1); - DValue* key = toElem(e->e2); + assert(aatype->ty == Taarray); + Type *indexType = static_cast(aatype)->index; + assert(indexType && vtype); - result = DtoAARemove(e->loc, aa, key); - } + llvm::Function *func = LLVM_D_GetRuntimeFunction( + e->loc, gIR->module, "_d_assocarrayliteralTX"); + LLFunctionType *funcTy = func->getFunctionType(); + LLValue *aaTypeInfo = + DtoBitCast(DtoTypeInfoOf(stripModifiers(aatype)), + DtoType(Type::typeinfoassociativearray->type)); - ////////////////////////////////////////////////////////////////////////////////////////// + LLConstant *idxs[2] = {DtoConstUint(0), DtoConstUint(0)}; - /// Constructs an array initializer constant with the given constants as its - /// elements. If the element types differ (unions, …), an anonymous struct - /// literal is emitted (as for array constant initializers). - llvm::Constant* arrayConst(std::vector& vals, Type* nominalElemType) - { - if (vals.size() == 0) - { - llvm::ArrayType* type = llvm::ArrayType::get(DtoType(nominalElemType), 0); - return llvm::ConstantArray::get(type, vals); - } - - llvm::Type* elementType = NULL; - bool differentTypes = false; - for (auto v : vals) - { - if (!elementType) - elementType = v->getType(); - else - differentTypes |= (elementType != v->getType()); - } - - if (differentTypes) - return llvm::ConstantStruct::getAnon(vals, true); - - llvm::ArrayType *t = llvm::ArrayType::get(elementType, vals.size()); - return llvm::ConstantArray::get(t, vals); - } - - void visit(AssocArrayLiteralExp *e) - { - IF_LOG Logger::print("AssocArrayLiteralExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - assert(e->keys); - assert(e->values); - assert(e->keys->dim == e->values->dim); - - Type* basetype = e->type->toBasetype(); - Type* aatype = basetype; - Type* vtype = aatype->nextOf(); - - if (!e->keys->dim) - goto LruntimeInit; - - if (aatype->ty != Taarray) { - // It's the AssociativeArray type. - // Turn it back into a TypeAArray - vtype = e->values->tdata()[0]->type; - aatype = new TypeAArray(vtype, e->keys->tdata()[0]->type); - aatype = aatype->semantic(e->loc, NULL); - } - - { - std::vector keysInits, valuesInits; - keysInits.reserve(e->keys->dim); - valuesInits.reserve(e->keys->dim); - for (size_t i = 0, n = e->keys->dim; i < n; ++i) - { - Expression* ekey = e->keys->tdata()[i]; - Expression* eval = e->values->tdata()[i]; - IF_LOG Logger::println("(%llu) aa[%s] = %s", static_cast(i), ekey->toChars(), eval->toChars()); - unsigned errors = global.startGagging(); - LLConstant *ekeyConst = toConstElem(ekey, p); - LLConstant *evalConst = toConstElem(eval, p); - if (global.endGagging(errors)) - goto LruntimeInit; - assert(ekeyConst && evalConst); - keysInits.push_back(ekeyConst); - valuesInits.push_back(evalConst); - } - - assert(aatype->ty == Taarray); - Type* indexType = static_cast(aatype)->index; - assert(indexType && vtype); - - llvm::Function* func = LLVM_D_GetRuntimeFunction(e->loc, gIR->module, "_d_assocarrayliteralTX"); - LLFunctionType* funcTy = func->getFunctionType(); - LLValue* aaTypeInfo = DtoBitCast(DtoTypeInfoOf(stripModifiers(aatype)), - DtoType(Type::typeinfoassociativearray->type)); - - LLConstant* idxs[2] = { DtoConstUint(0), DtoConstUint(0) }; - - LLConstant* initval = arrayConst(keysInits, indexType); - LLConstant* globalstore = new LLGlobalVariable(gIR->module, initval->getType(), - false, LLGlobalValue::InternalLinkage, initval, ".aaKeysStorage"); + LLConstant *initval = arrayConst(keysInits, indexType); + LLConstant *globalstore = new LLGlobalVariable( + gIR->module, initval->getType(), false, + LLGlobalValue::InternalLinkage, initval, ".aaKeysStorage"); #if LDC_LLVM_VER >= 307 - LLConstant* slice = llvm::ConstantExpr::getGetElementPtr(isaPointer(globalstore)->getElementType(), globalstore, idxs, true); + LLConstant *slice = llvm::ConstantExpr::getGetElementPtr( + isaPointer(globalstore)->getElementType(), globalstore, idxs, true); #else - LLConstant* slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); + LLConstant *slice = + llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); #endif - slice = DtoConstSlice(DtoConstSize_t(e->keys->dim), slice); - LLValue* keysArray = DtoAggrPaint(slice, funcTy->getParamType(1)); + slice = DtoConstSlice(DtoConstSize_t(e->keys->dim), slice); + LLValue *keysArray = DtoAggrPaint(slice, funcTy->getParamType(1)); - initval = arrayConst(valuesInits, vtype); - globalstore = new LLGlobalVariable(gIR->module, initval->getType(), - false, LLGlobalValue::InternalLinkage, initval, ".aaValuesStorage"); + initval = arrayConst(valuesInits, vtype); + globalstore = new LLGlobalVariable(gIR->module, initval->getType(), false, + LLGlobalValue::InternalLinkage, + initval, ".aaValuesStorage"); #if LDC_LLVM_VER >= 307 - slice = llvm::ConstantExpr::getGetElementPtr(isaPointer(globalstore)->getElementType(), globalstore, idxs, true); + slice = llvm::ConstantExpr::getGetElementPtr( + isaPointer(globalstore)->getElementType(), globalstore, idxs, true); #else - slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); + slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); #endif - slice = DtoConstSlice(DtoConstSize_t(e->keys->dim), slice); - LLValue* valuesArray = DtoAggrPaint(slice, funcTy->getParamType(2)); + slice = DtoConstSlice(DtoConstSize_t(e->keys->dim), slice); + LLValue *valuesArray = DtoAggrPaint(slice, funcTy->getParamType(2)); - LLValue* aa = gIR->CreateCallOrInvoke(func, aaTypeInfo, keysArray, valuesArray, "aa").getInstruction(); - if (basetype->ty != Taarray) { - LLValue *tmp = DtoAlloca(e->type, "aaliteral"); - DtoStore(aa, DtoGEPi(tmp, 0, 0)); - result = new DVarValue(e->type, tmp); - } else { - result = new DImValue(e->type, aa); - } - - return; - } - - LruntimeInit: - - // it should be possible to avoid the temporary in some cases - LLValue* tmp = DtoAllocaDump(LLConstant::getNullValue(DtoType(e->type)), e->type, "aaliteral"); + LLValue *aa = gIR->CreateCallOrInvoke(func, aaTypeInfo, keysArray, + valuesArray, "aa") + .getInstruction(); + if (basetype->ty != Taarray) { + LLValue *tmp = DtoAlloca(e->type, "aaliteral"); + DtoStore(aa, DtoGEPi(tmp, 0, 0)); result = new DVarValue(e->type, tmp); + } else { + result = new DImValue(e->type, aa); + } - const size_t n = e->keys->dim; - for (size_t i = 0; ikeys)[i]; - Expression* eval = (*e->values)[i]; - - IF_LOG Logger::println("(%llu) aa[%s] = %s", static_cast(i), ekey->toChars(), eval->toChars()); - - // index - DValue* key = toElem(ekey); - DValue* mem = DtoAAIndex(e->loc, vtype, result, key, true); - - // store - DValue* val = toElem(eval); - DtoAssign(e->loc, mem, val); - } + return; } - ////////////////////////////////////////////////////////////////////////////////////////// + LruntimeInit: - DValue* toGEP(UnaExp *exp, unsigned index) - { - // (&a.foo).funcptr is a case where toElem(e1) is genuinely not an l-value. - LLValue* val = makeLValue(exp->loc, toElem(exp->e1)); - LLValue* v = DtoGEPi(val, 0, index); - return new DVarValue(exp->type, DtoBitCast(v, DtoPtrToType(exp->type))); + // it should be possible to avoid the temporary in some cases + LLValue *tmp = DtoAllocaDump(LLConstant::getNullValue(DtoType(e->type)), + e->type, "aaliteral"); + result = new DVarValue(e->type, tmp); + + const size_t n = e->keys->dim; + for (size_t i = 0; i < n; ++i) { + Expression *ekey = (*e->keys)[i]; + Expression *eval = (*e->values)[i]; + + IF_LOG Logger::println("(%llu) aa[%s] = %s", + static_cast(i), + ekey->toChars(), eval->toChars()); + + // index + DValue *key = toElem(ekey); + DValue *mem = DtoAAIndex(e->loc, vtype, result, key, true); + + // store + DValue *val = toElem(eval); + DtoAssign(e->loc, mem, val); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + DValue *toGEP(UnaExp *exp, unsigned index) { + // (&a.foo).funcptr is a case where toElem(e1) is genuinely not an l-value. + LLValue *val = makeLValue(exp->loc, toElem(exp->e1)); + LLValue *v = DtoGEPi(val, 0, index); + return new DVarValue(exp->type, DtoBitCast(v, DtoPtrToType(exp->type))); + } + + void visit(DelegatePtrExp *e) { + IF_LOG Logger::print("DelegatePtrExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + result = toGEP(e, 0); + } + + void visit(DelegateFuncptrExp *e) { + IF_LOG Logger::print("DelegateFuncptrExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + result = toGEP(e, 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(BoolExp *e) { + IF_LOG Logger::print("BoolExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + result = new DImValue( + e->type, DtoCast(e->loc, toElem(e->e1), Type::tbool)->getRVal()); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(DotTypeExp *e) { + IF_LOG Logger::print("DotTypeExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + assert(e->sym->getType()); + result = toElem(e->e1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(TypeExp *e) { + e->error("type %s is not an expression", e->toChars()); + // TODO: Improve error handling. DMD just returns some value here and hopes + // some more sensible error messages will be triggered. + fatal(); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(TupleExp *e) { + IF_LOG Logger::print("TupleExp::toElem() %s\n", e->toChars()); + LOG_SCOPE; + + // If there are any side effects, evaluate them first. + if (e->e0) + toElem(e->e0); + + std::vector types; + types.reserve(e->exps->dim); + for (size_t i = 0; i < e->exps->dim; i++) { + types.push_back(DtoMemType((*e->exps)[i]->type)); + } + LLValue *val = + DtoRawAlloca(LLStructType::get(gIR->context(), types), 0, ".tuple"); + for (size_t i = 0; i < e->exps->dim; i++) { + Expression *el = (*e->exps)[i]; + DValue *ep = toElem(el); + LLValue *gep = DtoGEPi(val, 0, i); + if (DtoIsPassedByRef(el->type)) + DtoStore(DtoLoad(ep->getRVal()), gep); + else if (el->type->ty != Tvoid) + DtoStoreZextI8(ep->getRVal(), gep); + else + DtoStore(LLConstantInt::get(LLType::getInt8Ty(p->context()), 0, false), + gep); + } + result = new DVarValue(e->type, val); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(VectorExp *e) { + IF_LOG Logger::print("VectorExp::toElem() %s\n", e->toChars()); + LOG_SCOPE; + + TypeVector *type = static_cast(e->to->toBasetype()); + assert(e->type->ty == Tvector); + + LLValue *vector = DtoAlloca(e->to); + + // Array literals are assigned element-wise, other expressions are cast and + // splat across the vector elements. This is what DMD does. + if (e->e1->op == TOKarrayliteral) { + Logger::println("array literal expression"); + ArrayLiteralExp *lit = static_cast(e->e1); + assert(lit->elements->dim == e->dim && + "Array literal vector initializer " + "length mismatch, should have been handled in frontend."); + for (unsigned int i = 0; i < e->dim; ++i) { + DValue *val = toElem((*lit->elements)[i]); + LLValue *llval = DtoCast(e->loc, val, type->elementType())->getRVal(); + DtoStore(llval, DtoGEPi(vector, 0, i)); + } + } else { + Logger::println("normal (splat) expression"); + DValue *val = toElem(e->e1); + LLValue *llval = DtoCast(e->loc, val, type->elementType())->getRVal(); + for (unsigned int i = 0; i < e->dim; ++i) { + DtoStore(llval, DtoGEPi(vector, 0, i)); + } } - void visit(DelegatePtrExp *e) - { - IF_LOG Logger::print("DelegatePtrExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + result = new DVarValue(e->to, vector); + } - result = toGEP(e, 0); + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(PowExp *e) { + IF_LOG Logger::print("PowExp::toElem() %s\n", e->toChars()); + LOG_SCOPE; + + e->error("must import std.math to use ^^ operator"); + result = new DNullValue(e->type, llvm::UndefValue::get(DtoType(e->type))); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + void visit(TypeidExp *e) { + if (Type *t = isType(e->obj)) { + result = DtoSymbolAddress(e->loc, e->type, + getOrCreateTypeInfoDeclaration(t, NULL)); + return; } + if (Expression *ex = isExpression(e->obj)) { + Type *t = ex->type->toBasetype(); + assert(t->ty == Tclass); - void visit(DelegateFuncptrExp *e) - { - IF_LOG Logger::print("DelegateFuncptrExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; + DValue *val = toElem(ex); - result = toGEP(e, 1); + // Get and load vtbl pointer. + llvm::Value *vtbl = DtoLoad(DtoGEPi(val->getRVal(), 0, 0)); + + // TypeInfo ptr is first vtbl entry. + llvm::Value *typinf = DtoGEPi(vtbl, 0, 0); + + Type *resultType = Type::typeinfoclass->type; + if (static_cast(t)->sym->isInterfaceDeclaration()) { + // For interfaces, the first entry in the vtbl is actually a pointer + // to an Interface instance, which has the type info as its first + // member, so we have to add an extra layer of indirection. + resultType = Type::typeinfointerface->type; + typinf = DtoLoad( + DtoBitCast(typinf, DtoType(resultType->pointerTo()->pointerTo()))); + } + + result = new DVarValue(resultType, typinf); + return; } + llvm_unreachable("Unknown TypeidExp argument kind"); + } - ////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// - void visit(BoolExp *e) - { - IF_LOG Logger::print("BoolExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - result = new DImValue(e->type, DtoCast(e->loc, toElem(e->e1), Type::tbool)->getRVal()); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(DotTypeExp *e) - { - IF_LOG Logger::print("DotTypeExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); - LOG_SCOPE; - - assert(e->sym->getType()); - result = toElem(e->e1); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(TypeExp *e) - { - e->error("type %s is not an expression", e->toChars()); - //TODO: Improve error handling. DMD just returns some value here and hopes - // some more sensible error messages will be triggered. - fatal(); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(TupleExp *e) - { - IF_LOG Logger::print("TupleExp::toElem() %s\n", e->toChars()); - LOG_SCOPE; - - // If there are any side effects, evaluate them first. - if (e->e0) toElem(e->e0); - - std::vector types; - types.reserve(e->exps->dim); - for (size_t i = 0; i < e->exps->dim; i++) - { - types.push_back(DtoMemType((*e->exps)[i]->type)); - } - LLValue *val = DtoRawAlloca(LLStructType::get(gIR->context(), types), 0, ".tuple"); - for (size_t i = 0; i < e->exps->dim; i++) - { - Expression *el = (*e->exps)[i]; - DValue* ep = toElem(el); - LLValue *gep = DtoGEPi(val,0,i); - if (DtoIsPassedByRef(el->type)) - DtoStore(DtoLoad(ep->getRVal()), gep); - else if (el->type->ty != Tvoid) - DtoStoreZextI8(ep->getRVal(), gep); - else - DtoStore(LLConstantInt::get(LLType::getInt8Ty(p->context()), 0, false), gep); - } - result = new DVarValue(e->type, val); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(VectorExp *e) - { - IF_LOG Logger::print("VectorExp::toElem() %s\n", e->toChars()); - LOG_SCOPE; - - TypeVector *type = static_cast(e->to->toBasetype()); - assert(e->type->ty == Tvector); - - LLValue *vector = DtoAlloca(e->to); - - // Array literals are assigned element-wise, other expressions are cast and - // splat across the vector elements. This is what DMD does. - if (e->e1->op == TOKarrayliteral) { - Logger::println("array literal expression"); - ArrayLiteralExp *lit = static_cast(e->e1); - assert(lit->elements->dim == e->dim && "Array literal vector initializer " - "length mismatch, should have been handled in frontend."); - for (unsigned int i = 0; i < e->dim; ++i) { - DValue *val = toElem((*lit->elements)[i]); - LLValue *llval = DtoCast(e->loc, val, type->elementType())->getRVal(); - DtoStore(llval, DtoGEPi(vector, 0, i)); - } - } else { - Logger::println("normal (splat) expression"); - DValue *val = toElem(e->e1); - LLValue* llval = DtoCast(e->loc, val, type->elementType())->getRVal(); - for (unsigned int i = 0; i < e->dim; ++i) { - DtoStore(llval, DtoGEPi(vector, 0, i)); - } - } - - result = new DVarValue(e->to, vector); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(PowExp *e) - { - IF_LOG Logger::print("PowExp::toElem() %s\n", e->toChars()); - LOG_SCOPE; - - e->error("must import std.math to use ^^ operator"); - result = new DNullValue(e->type, llvm::UndefValue::get(DtoType(e->type))); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - void visit(TypeidExp *e) - { - if (Type *t = isType(e->obj)) - { - result = DtoSymbolAddress(e->loc, e->type, - getOrCreateTypeInfoDeclaration(t, NULL)); - return; - } - if (Expression *ex = isExpression(e->obj)) - { - Type *t = ex->type->toBasetype(); - assert(t->ty == Tclass); - - DValue *val = toElem(ex); - - // Get and load vtbl pointer. - llvm::Value *vtbl = DtoLoad(DtoGEPi(val->getRVal(), 0, 0)); - - // TypeInfo ptr is first vtbl entry. - llvm::Value *typinf = DtoGEPi(vtbl, 0, 0); - - Type *resultType = Type::typeinfoclass->type; - if (static_cast(t)->sym->isInterfaceDeclaration()) - { - // For interfaces, the first entry in the vtbl is actually a pointer - // to an Interface instance, which has the type info as its first - // member, so we have to add an extra layer of indirection. - resultType = Type::typeinfointerface->type; - typinf = DtoLoad(DtoBitCast(typinf, - DtoType(resultType->pointerTo()->pointerTo()))); - } - - result = new DVarValue(resultType, typinf); - return; - } - llvm_unreachable("Unknown TypeidExp argument kind"); - } - - ////////////////////////////////////////////////////////////////////////////////////////// - - #define STUB(x) void visit(x * e) { e->error("Internal compiler error: Type "#x" not implemented: %s", e->toChars()); fatal(); } - STUB(Expression) - STUB(ScopeExp) - STUB(SymbolExp) - STUB(PowAssignExp) +#define STUB(x) \ + void visit(x *e) { \ + e->error("Internal compiler error: Type " #x " not implemented: %s", \ + e->toChars()); \ + fatal(); \ + } + STUB(Expression) + STUB(ScopeExp) + STUB(SymbolExp) + STUB(PowAssignExp) }; ////////////////////////////////////////////////////////////////////////////////////////////// -DValue *toElem(Expression *e) -{ - ToElemVisitor v(gIR, false); - e->accept(&v); - return v.getResult(); +DValue *toElem(Expression *e) { + ToElemVisitor v(gIR, false); + e->accept(&v); + return v.getResult(); } -DValue *toElemDtor(Expression *e) -{ - ToElemVisitor v(gIR, true); - e->accept(&v); - return v.getResult(); +DValue *toElemDtor(Expression *e) { + ToElemVisitor v(gIR, true); + e->accept(&v); + return v.getResult(); } // FIXME: Implement & place in right module -Symbol *toModuleAssert(Module *m) -{ - return NULL; -} +Symbol *toModuleAssert(Module *m) { return NULL; } // FIXME: Implement & place in right module -Symbol *toModuleUnittest(Module *m) -{ - return NULL; -} +Symbol *toModuleUnittest(Module *m) { return NULL; } // FIXME: Implement & place in right module -Symbol *toModuleArray(Module *m) -{ - return NULL; -} +Symbol *toModuleArray(Module *m) { return NULL; } diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 4307be0a65..4aa7376ffb 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -34,815 +34,720 @@ #include "ir/irtypefunction.h" #include "ir/irtypestruct.h" -bool DtoIsPassedByRef(Type* type) -{ - Type* typ = type->toBasetype(); - TY t = typ->ty; - return (t == Tstruct || t == Tsarray); +bool DtoIsPassedByRef(Type *type) { + Type *typ = type->toBasetype(); + TY t = typ->ty; + return (t == Tstruct || t == Tsarray); } -RET retStyle(TypeFunction *tf) -{ - bool sret = gABI->returnInArg(tf); - return sret ? RETstack : RETregs; +RET retStyle(TypeFunction *tf) { + bool sret = gABI->returnInArg(tf); + return sret ? RETstack : RETregs; } -bool DtoIsReturnInArg(CallExp *ce) -{ - TypeFunction *tf = static_cast(ce->e1->type->toBasetype()); - if (tf->ty == Tfunction && (!ce->f || !DtoIsIntrinsic(ce->f))) - return retStyle(tf) == RETstack; - return false; +bool DtoIsReturnInArg(CallExp *ce) { + TypeFunction *tf = static_cast(ce->e1->type->toBasetype()); + if (tf->ty == Tfunction && (!ce->f || !DtoIsIntrinsic(ce->f))) + return retStyle(tf) == RETstack; + return false; } -LLAttribute DtoShouldExtend(Type* type) -{ - type = type->toBasetype(); - if (type->isintegral()) - { - switch(type->ty) - { - case Tint8: - case Tint16: - return LLAttribute::SExt; - - case Tuns8: - case Tuns16: - return LLAttribute::ZExt; - - default: - // Do not extend. - break; - } - } - - return LLAttribute::None; -} - -LLType* DtoType(Type* t) -{ - t = stripModifiers( t ); - - if (t->ctype) - { - return t->ctype->getLLType(); - } - - IF_LOG Logger::println("Building type: %s", t->toChars()); - LOG_SCOPE; - - assert(t); - switch (t->ty) - { - // basic types - case Tvoid: +LLAttribute DtoShouldExtend(Type *type) { + type = type->toBasetype(); + if (type->isintegral()) { + switch (type->ty) { case Tint8: - case Tuns8: case Tint16: + return LLAttribute::SExt; + + case Tuns8: case Tuns16: - case Tint32: - case Tuns32: - case Tint64: - case Tuns64: - case Tint128: - case Tuns128: - case Tfloat32: - case Tfloat64: - case Tfloat80: - case Timaginary32: - case Timaginary64: - case Timaginary80: - case Tcomplex32: - case Tcomplex64: - case Tcomplex80: - //case Tbit: - case Tbool: - case Tchar: - case Twchar: - case Tdchar: - { - return IrTypeBasic::get(t)->getLLType(); - } - - // pointers - case Tnull: - case Tpointer: - { - return IrTypePointer::get(t)->getLLType(); - } - - // arrays - case Tarray: - { - return IrTypeArray::get(t)->getLLType(); - } - - case Tsarray: - { - return IrTypeSArray::get(t)->getLLType(); - } - - // aggregates - case Tstruct: - { - TypeStruct* ts = static_cast(t); - if (ts->sym->type->ctype) - { - // This should not happen, but the frontend seems to be buggy. Not - // sure if this is the best way to handle the situation, but we - // certainly don't want to override ts->sym->type->ctype. - IF_LOG Logger::cout() << "Struct with multiple Types detected: " << - ts->toChars() << " (" << ts->sym->locToChars() << ")" << std::endl; - return ts->sym->type->ctype->getLLType(); - } - return IrTypeStruct::get(ts->sym)->getLLType(); - } - case Tclass: - { - TypeClass* tc = static_cast(t); - if (tc->sym->type->ctype) - { - // See Tstruct case. - IF_LOG Logger::cout() << "Class with multiple Types detected: " << - tc->toChars() << " (" << tc->sym->locToChars() << ")" << std::endl; - return tc->sym->type->ctype->getLLType(); - } - return IrTypeClass::get(tc->sym)->getLLType(); - } - - // functions - case Tfunction: - { - return IrTypeFunction::get(t)->getLLType(); - } - - // delegates - case Tdelegate: - { - return IrTypeDelegate::get(t)->getLLType(); - } - - // typedefs - // enum - - // FIXME: maybe just call toBasetype first ? - case Tenum: - { - Type* bt = t->toBasetype(); - assert(bt); - return DtoType(bt); - } - - // associative arrays - case Taarray: - return getVoidPtrType(); - - case Tvector: - { - return IrTypeVector::get(t)->getLLType(); - } + return LLAttribute::ZExt; default: - llvm_unreachable("Unknown class of D Type!"); + // Do not extend. + break; } - return 0; + } + + return LLAttribute::None; } -LLType* DtoMemType(Type* t) -{ - return i1ToI8(voidToI8(DtoType(t))); -} +LLType *DtoType(Type *t) { + t = stripModifiers(t); -LLPointerType* DtoPtrToType(Type* t) -{ - return DtoMemType(t)->getPointerTo(); -} + if (t->ctype) { + return t->ctype->getLLType(); + } -LLType* voidToI8(LLType* t) -{ - if (t == LLType::getVoidTy(gIR->context())) - return LLType::getInt8Ty(gIR->context()); - return t; -} + IF_LOG Logger::println("Building type: %s", t->toChars()); + LOG_SCOPE; -LLType* i1ToI8(LLType* t) -{ - if (t == LLType::getInt1Ty(gIR->context())) - return LLType::getInt8Ty(gIR->context()); - return t; -} + assert(t); + switch (t->ty) { + // basic types + case Tvoid: + case Tint8: + case Tuns8: + case Tint16: + case Tuns16: + case Tint32: + case Tuns32: + case Tint64: + case Tuns64: + case Tint128: + case Tuns128: + case Tfloat32: + case Tfloat64: + case Tfloat80: + case Timaginary32: + case Timaginary64: + case Timaginary80: + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + // case Tbit: + case Tbool: + case Tchar: + case Twchar: + case Tdchar: { + return IrTypeBasic::get(t)->getLLType(); + } -////////////////////////////////////////////////////////////////////////////////////////// + // pointers + case Tnull: + case Tpointer: { + return IrTypePointer::get(t)->getLLType(); + } -LLValue* DtoDelegateEquals(TOK op, LLValue* lhs, LLValue* rhs) -{ - Logger::println("Doing delegate equality"); - llvm::Value *b1, *b2; - if (rhs == NULL) - { - rhs = LLConstant::getNullValue(lhs->getType()); + // arrays + case Tarray: { + return IrTypeArray::get(t)->getLLType(); + } + + case Tsarray: { + return IrTypeSArray::get(t)->getLLType(); + } + + // aggregates + case Tstruct: { + TypeStruct *ts = static_cast(t); + if (ts->sym->type->ctype) { + // This should not happen, but the frontend seems to be buggy. Not + // sure if this is the best way to handle the situation, but we + // certainly don't want to override ts->sym->type->ctype. + IF_LOG Logger::cout() + << "Struct with multiple Types detected: " << ts->toChars() << " (" + << ts->sym->locToChars() << ")" << std::endl; + return ts->sym->type->ctype->getLLType(); } - - LLValue* l = gIR->ir->CreateExtractValue(lhs, 0); - LLValue* r = gIR->ir->CreateExtractValue(rhs, 0); - b1 = gIR->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ,l,r); - - l = gIR->ir->CreateExtractValue(lhs, 1); - r = gIR->ir->CreateExtractValue(rhs, 1); - b2 = gIR->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ,l,r); - - LLValue* b = gIR->ir->CreateAnd(b1,b2); - - if (op == TOKnotequal || op == TOKnotidentity) - return gIR->ir->CreateNot(b); - - return b; -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LinkageWithCOMDAT DtoLinkage(Dsymbol* sym) -{ - if (DtoIsTemplateInstance(sym)) - return LinkageWithCOMDAT(templateLinkage, supportsCOMDAT()); - return LinkageWithCOMDAT(llvm::GlobalValue::ExternalLinkage, false); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LLIntegerType* DtoSize_t() -{ - // the type of size_t does not change once set - static LLIntegerType* t = NULL; - if (t == NULL) - t = (global.params.isLP64) ? LLType::getInt64Ty(gIR->context()) : LLType::getInt32Ty(gIR->context()); - return t; -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LLValue* DtoGEP1(LLValue* ptr, LLValue* i0, const char* var, llvm::BasicBlock* bb) -{ - LLPointerType* p = isaPointer(ptr); - assert(p && "GEP expects a pointer type"); - return llvm::GetElementPtrInst::Create( -#if LDC_LLVM_VER >= 307 - p->getElementType(), -#endif - ptr, i0, var, bb ? bb : gIR->scopebb()); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LLValue* DtoGEP(LLValue* ptr, LLValue* i0, LLValue* i1, const char* var, llvm::BasicBlock* bb) -{ - LLPointerType* p = isaPointer(ptr); - assert(p && "GEP expects a pointer type"); - LLValue* v[] = { i0, i1 }; - return llvm::GetElementPtrInst::Create( -#if LDC_LLVM_VER >= 307 - p->getElementType(), -#endif - ptr, v, var, bb ? bb : gIR->scopebb()); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LLValue* DtoGEPi1(LLValue* ptr, unsigned i, const char* var, llvm::BasicBlock* bb) -{ - LLPointerType* p = isaPointer(ptr); - assert(p && "GEP expects a pointer type"); - return llvm::GetElementPtrInst::Create( -#if LDC_LLVM_VER >= 307 - p->getElementType(), -#endif - ptr, DtoConstUint(i), var, bb ? bb : gIR->scopebb()); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LLValue* DtoGEPi(LLValue* ptr, unsigned i0, unsigned i1, const char* var, llvm::BasicBlock* bb) -{ - LLPointerType* p = isaPointer(ptr); - assert(p && "GEP expects a pointer type"); - LLValue* v[] = { DtoConstUint(i0), DtoConstUint(i1) }; - return llvm::GetElementPtrInst::Create( -#if LDC_LLVM_VER >= 307 - p->getElementType(), -#endif - ptr, v, var, bb ? bb : gIR->scopebb()); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LLConstant* DtoGEPi(LLConstant* ptr, unsigned i0, unsigned i1) -{ - LLPointerType* p = isaPointer(ptr); - assert(p && "GEP expects a pointer type"); - LLValue* v[] = { DtoConstUint(i0), DtoConstUint(i1) }; - return llvm::ConstantExpr::getGetElementPtr( -#if LDC_LLVM_VER >= 307 - p->getElementType(), -#endif - ptr, v, true); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -void DtoMemSet(LLValue* dst, LLValue* val, LLValue* nbytes) -{ - LLType* VoidPtrTy = getVoidPtrType(); - - dst = DtoBitCast(dst, VoidPtrTy); - - gIR->ir->CreateMemSet(dst, val, nbytes, 1 /*Align*/, false /*isVolatile*/); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -void DtoMemSetZero(LLValue* dst, LLValue* nbytes) -{ - DtoMemSet(dst, DtoConstUbyte(0), nbytes); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes, unsigned align) -{ - LLType* VoidPtrTy = getVoidPtrType(); - - dst = DtoBitCast(dst, VoidPtrTy); - src = DtoBitCast(src, VoidPtrTy); - - gIR->ir->CreateMemCpy(dst, src, nbytes, align, false /*isVolatile*/); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LLValue* DtoMemCmp(LLValue* lhs, LLValue* rhs, LLValue* nbytes) -{ - // int memcmp ( const void * ptr1, const void * ptr2, size_t num ); - - LLType* VoidPtrTy = getVoidPtrType(); - LLFunction* fn = gIR->module.getFunction("memcmp"); - if (!fn) - { - LLType* Tys[] = { VoidPtrTy, VoidPtrTy, DtoSize_t() }; - LLFunctionType* fty = LLFunctionType::get(LLType::getInt32Ty(gIR->context()), - Tys, false); - fn = LLFunction::Create(fty, LLGlobalValue::ExternalLinkage, "memcmp", &gIR->module); + return IrTypeStruct::get(ts->sym)->getLLType(); + } + case Tclass: { + TypeClass *tc = static_cast(t); + if (tc->sym->type->ctype) { + // See Tstruct case. + IF_LOG Logger::cout() + << "Class with multiple Types detected: " << tc->toChars() << " (" + << tc->sym->locToChars() << ")" << std::endl; + return tc->sym->type->ctype->getLLType(); } + return IrTypeClass::get(tc->sym)->getLLType(); + } - lhs = DtoBitCast(lhs, VoidPtrTy); - rhs = DtoBitCast(rhs, VoidPtrTy); + // functions + case Tfunction: { + return IrTypeFunction::get(t)->getLLType(); + } + + // delegates + case Tdelegate: { + return IrTypeDelegate::get(t)->getLLType(); + } + + // typedefs + // enum + + // FIXME: maybe just call toBasetype first ? + case Tenum: { + Type *bt = t->toBasetype(); + assert(bt); + return DtoType(bt); + } + + // associative arrays + case Taarray: + return getVoidPtrType(); + + case Tvector: { + return IrTypeVector::get(t)->getLLType(); + } + + default: + llvm_unreachable("Unknown class of D Type!"); + } + return 0; +} + +LLType *DtoMemType(Type *t) { return i1ToI8(voidToI8(DtoType(t))); } + +LLPointerType *DtoPtrToType(Type *t) { return DtoMemType(t)->getPointerTo(); } + +LLType *voidToI8(LLType *t) { + if (t == LLType::getVoidTy(gIR->context())) + return LLType::getInt8Ty(gIR->context()); + return t; +} + +LLType *i1ToI8(LLType *t) { + if (t == LLType::getInt1Ty(gIR->context())) + return LLType::getInt8Ty(gIR->context()); + return t; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLValue *DtoDelegateEquals(TOK op, LLValue *lhs, LLValue *rhs) { + Logger::println("Doing delegate equality"); + llvm::Value *b1, *b2; + if (rhs == NULL) { + rhs = LLConstant::getNullValue(lhs->getType()); + } + + LLValue *l = gIR->ir->CreateExtractValue(lhs, 0); + LLValue *r = gIR->ir->CreateExtractValue(rhs, 0); + b1 = gIR->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, l, r); + + l = gIR->ir->CreateExtractValue(lhs, 1); + r = gIR->ir->CreateExtractValue(rhs, 1); + b2 = gIR->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, l, r); + + LLValue *b = gIR->ir->CreateAnd(b1, b2); + + if (op == TOKnotequal || op == TOKnotidentity) + return gIR->ir->CreateNot(b); + + return b; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) { + if (DtoIsTemplateInstance(sym)) + return LinkageWithCOMDAT(templateLinkage, supportsCOMDAT()); + return LinkageWithCOMDAT(llvm::GlobalValue::ExternalLinkage, false); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLIntegerType *DtoSize_t() { + // the type of size_t does not change once set + static LLIntegerType *t = NULL; + if (t == NULL) + t = (global.params.isLP64) ? LLType::getInt64Ty(gIR->context()) + : LLType::getInt32Ty(gIR->context()); + return t; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLValue *DtoGEP1(LLValue *ptr, LLValue *i0, const char *var, + llvm::BasicBlock *bb) { + LLPointerType *p = isaPointer(ptr); + assert(p && "GEP expects a pointer type"); + return llvm::GetElementPtrInst::Create( +#if LDC_LLVM_VER >= 307 + p->getElementType(), +#endif + ptr, i0, var, bb ? bb : gIR->scopebb()); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLValue *DtoGEP(LLValue *ptr, LLValue *i0, LLValue *i1, const char *var, + llvm::BasicBlock *bb) { + LLPointerType *p = isaPointer(ptr); + assert(p && "GEP expects a pointer type"); + LLValue *v[] = {i0, i1}; + return llvm::GetElementPtrInst::Create( +#if LDC_LLVM_VER >= 307 + p->getElementType(), +#endif + ptr, v, var, bb ? bb : gIR->scopebb()); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLValue *DtoGEPi1(LLValue *ptr, unsigned i, const char *var, + llvm::BasicBlock *bb) { + LLPointerType *p = isaPointer(ptr); + assert(p && "GEP expects a pointer type"); + return llvm::GetElementPtrInst::Create( +#if LDC_LLVM_VER >= 307 + p->getElementType(), +#endif + ptr, DtoConstUint(i), var, bb ? bb : gIR->scopebb()); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLValue *DtoGEPi(LLValue *ptr, unsigned i0, unsigned i1, const char *var, + llvm::BasicBlock *bb) { + LLPointerType *p = isaPointer(ptr); + assert(p && "GEP expects a pointer type"); + LLValue *v[] = {DtoConstUint(i0), DtoConstUint(i1)}; + return llvm::GetElementPtrInst::Create( +#if LDC_LLVM_VER >= 307 + p->getElementType(), +#endif + ptr, v, var, bb ? bb : gIR->scopebb()); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLConstant *DtoGEPi(LLConstant *ptr, unsigned i0, unsigned i1) { + LLPointerType *p = isaPointer(ptr); + assert(p && "GEP expects a pointer type"); + LLValue *v[] = {DtoConstUint(i0), DtoConstUint(i1)}; + return llvm::ConstantExpr::getGetElementPtr( +#if LDC_LLVM_VER >= 307 + p->getElementType(), +#endif + ptr, v, true); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void DtoMemSet(LLValue *dst, LLValue *val, LLValue *nbytes) { + LLType *VoidPtrTy = getVoidPtrType(); + + dst = DtoBitCast(dst, VoidPtrTy); + + gIR->ir->CreateMemSet(dst, val, nbytes, 1 /*Align*/, false /*isVolatile*/); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void DtoMemSetZero(LLValue *dst, LLValue *nbytes) { + DtoMemSet(dst, DtoConstUbyte(0), nbytes); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void DtoMemCpy(LLValue *dst, LLValue *src, LLValue *nbytes, unsigned align) { + LLType *VoidPtrTy = getVoidPtrType(); + + dst = DtoBitCast(dst, VoidPtrTy); + src = DtoBitCast(src, VoidPtrTy); + + gIR->ir->CreateMemCpy(dst, src, nbytes, align, false /*isVolatile*/); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +LLValue *DtoMemCmp(LLValue *lhs, LLValue *rhs, LLValue *nbytes) { + // int memcmp ( const void * ptr1, const void * ptr2, size_t num ); + + LLType *VoidPtrTy = getVoidPtrType(); + LLFunction *fn = gIR->module.getFunction("memcmp"); + if (!fn) { + LLType *Tys[] = {VoidPtrTy, VoidPtrTy, DtoSize_t()}; + LLFunctionType *fty = + LLFunctionType::get(LLType::getInt32Ty(gIR->context()), Tys, false); + fn = LLFunction::Create(fty, LLGlobalValue::ExternalLinkage, "memcmp", + &gIR->module); + } + + lhs = DtoBitCast(lhs, VoidPtrTy); + rhs = DtoBitCast(rhs, VoidPtrTy); #if LDC_LLVM_VER >= 307 - return gIR->ir->CreateCall(fn, { lhs, rhs, nbytes }); + return gIR->ir->CreateCall(fn, {lhs, rhs, nbytes}); #else - return gIR->ir->CreateCall3(fn, lhs, rhs, nbytes); + return gIR->ir->CreateCall3(fn, lhs, rhs, nbytes); #endif } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoAggrZeroInit(LLValue* v) -{ - uint64_t n = getTypeStoreSize(v->getType()->getContainedType(0)); - DtoMemSetZero(v, DtoConstSize_t(n)); +void DtoAggrZeroInit(LLValue *v) { + uint64_t n = getTypeStoreSize(v->getType()->getContainedType(0)); + DtoMemSetZero(v, DtoConstSize_t(n)); } ////////////////////////////////////////////////////////////////////////////////////////// -void DtoAggrCopy(LLValue* dst, LLValue* src) -{ - uint64_t n = getTypeStoreSize(dst->getType()->getContainedType(0)); - DtoMemCpy(dst, src, DtoConstSize_t(n)); +void DtoAggrCopy(LLValue *dst, LLValue *src) { + uint64_t n = getTypeStoreSize(dst->getType()->getContainedType(0)); + DtoMemCpy(dst, src, DtoConstSize_t(n)); } ////////////////////////////////////////////////////////////////////////////////////////// -llvm::ConstantInt* DtoConstSize_t(uint64_t i) -{ - return LLConstantInt::get(DtoSize_t(), i, false); +llvm::ConstantInt *DtoConstSize_t(uint64_t i) { + return LLConstantInt::get(DtoSize_t(), i, false); } -llvm::ConstantInt* DtoConstUint(unsigned i) -{ - return LLConstantInt::get(LLType::getInt32Ty(gIR->context()), i, false); +llvm::ConstantInt *DtoConstUint(unsigned i) { + return LLConstantInt::get(LLType::getInt32Ty(gIR->context()), i, false); } -llvm::ConstantInt* DtoConstInt(int i) -{ - return LLConstantInt::get(LLType::getInt32Ty(gIR->context()), i, true); +llvm::ConstantInt *DtoConstInt(int i) { + return LLConstantInt::get(LLType::getInt32Ty(gIR->context()), i, true); } -LLConstant* DtoConstBool(bool b) -{ - return LLConstantInt::get(LLType::getInt1Ty(gIR->context()), b, false); +LLConstant *DtoConstBool(bool b) { + return LLConstantInt::get(LLType::getInt1Ty(gIR->context()), b, false); } -llvm::ConstantInt* DtoConstUbyte(unsigned char i) -{ - return LLConstantInt::get(LLType::getInt8Ty(gIR->context()), i, false); +llvm::ConstantInt *DtoConstUbyte(unsigned char i) { + return LLConstantInt::get(LLType::getInt8Ty(gIR->context()), i, false); } -LLConstant* DtoConstFP(Type* t, longdouble value) -{ - LLType* llty = DtoType(t); - assert(llty->isFloatingPointTy()); +LLConstant *DtoConstFP(Type *t, longdouble value) { + LLType *llty = DtoType(t); + assert(llty->isFloatingPointTy()); - if(llty == LLType::getFloatTy(gIR->context()) || llty == LLType::getDoubleTy(gIR->context())) - return LLConstantFP::get(llty, value); - else if(llty == LLType::getX86_FP80Ty(gIR->context())) { - uint64_t bits[] = { 0, 0 }; - bits[0] = *reinterpret_cast(&value); - bits[1] = *reinterpret_cast(reinterpret_cast(&value) + 1); - return LLConstantFP::get(gIR->context(), APFloat(APFloat::x87DoubleExtended, APInt(80, 2, bits))); - } else if(llty == LLType::getPPC_FP128Ty(gIR->context())) { - uint64_t bits[] = {0, 0}; - bits[0] = *reinterpret_cast(&value); - bits[1] = *reinterpret_cast(reinterpret_cast(&value) + 1); - return LLConstantFP::get(gIR->context(), APFloat(APFloat::PPCDoubleDouble, APInt(128, 2, bits))); - } + if (llty == LLType::getFloatTy(gIR->context()) || + llty == LLType::getDoubleTy(gIR->context())) + return LLConstantFP::get(llty, value); + else if (llty == LLType::getX86_FP80Ty(gIR->context())) { + uint64_t bits[] = {0, 0}; + bits[0] = *reinterpret_cast(&value); + bits[1] = + *reinterpret_cast(reinterpret_cast(&value) + 1); + return LLConstantFP::get(gIR->context(), APFloat(APFloat::x87DoubleExtended, + APInt(80, 2, bits))); + } else if (llty == LLType::getPPC_FP128Ty(gIR->context())) { + uint64_t bits[] = {0, 0}; + bits[0] = *reinterpret_cast(&value); + bits[1] = + *reinterpret_cast(reinterpret_cast(&value) + 1); + return LLConstantFP::get( + gIR->context(), APFloat(APFloat::PPCDoubleDouble, APInt(128, 2, bits))); + } - llvm_unreachable("Unknown floating point type encountered"); + llvm_unreachable("Unknown floating point type encountered"); } ////////////////////////////////////////////////////////////////////////////////////////// -LLConstant* DtoConstString(const char* str) -{ - llvm::StringRef s(str ? str : ""); - llvm::GlobalVariable* gvar = (gIR->stringLiteral1ByteCache.find(s) == - gIR->stringLiteral1ByteCache.end()) - ? 0 : gIR->stringLiteral1ByteCache[s]; - if (gvar == 0) - { - llvm::Constant* init = llvm::ConstantDataArray::getString(gIR->context(), s, true); - gvar = new llvm::GlobalVariable(gIR->module, init->getType(), true, - llvm::GlobalValue::PrivateLinkage, init, ".str"); - gvar->setUnnamedAddr(true); - gIR->stringLiteral1ByteCache[s] = gvar; - } - LLConstant* idxs[] = { DtoConstUint(0), DtoConstUint(0) }; - return DtoConstSlice( - DtoConstSize_t(s.size()), - llvm::ConstantExpr::getGetElementPtr( +LLConstant *DtoConstString(const char *str) { + llvm::StringRef s(str ? str : ""); + llvm::GlobalVariable *gvar = (gIR->stringLiteral1ByteCache.find(s) == + gIR->stringLiteral1ByteCache.end()) + ? 0 + : gIR->stringLiteral1ByteCache[s]; + if (gvar == 0) { + llvm::Constant *init = + llvm::ConstantDataArray::getString(gIR->context(), s, true); + gvar = new llvm::GlobalVariable(gIR->module, init->getType(), true, + llvm::GlobalValue::PrivateLinkage, init, + ".str"); + gvar->setUnnamedAddr(true); + gIR->stringLiteral1ByteCache[s] = gvar; + } + LLConstant *idxs[] = {DtoConstUint(0), DtoConstUint(0)}; + return DtoConstSlice(DtoConstSize_t(s.size()), + llvm::ConstantExpr::getGetElementPtr( #if LDC_LLVM_VER >= 307 - gvar->getInitializer()->getType(), + gvar->getInitializer()->getType(), #endif - gvar, idxs, true), - Type::tchar->arrayOf() - ); + gvar, idxs, true), + Type::tchar->arrayOf()); } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoLoad(LLValue* src, const char* name) -{ -// if (Logger::enabled()) -// Logger::cout() << "loading " << *src << '\n'; - llvm::LoadInst* ld = gIR->ir->CreateLoad(src, name); - //ld->setVolatile(gIR->func()->inVolatile); - return ld; +LLValue *DtoLoad(LLValue *src, const char *name) { + // if (Logger::enabled()) + // Logger::cout() << "loading " << *src << '\n'; + llvm::LoadInst *ld = gIR->ir->CreateLoad(src, name); + // ld->setVolatile(gIR->func()->inVolatile); + return ld; } -// Like DtoLoad, but the pointer is guaranteed to be aligned appropriately for the type. -LLValue* DtoAlignedLoad(LLValue* src, const char* name) -{ - llvm::LoadInst* ld = gIR->ir->CreateLoad(src, name); - ld->setAlignment(getABITypeAlign(ld->getType())); - return ld; +// Like DtoLoad, but the pointer is guaranteed to be aligned appropriately for +// the type. +LLValue *DtoAlignedLoad(LLValue *src, const char *name) { + llvm::LoadInst *ld = gIR->ir->CreateLoad(src, name); + ld->setAlignment(getABITypeAlign(ld->getType())); + return ld; } -LLValue* DtoVolatileLoad(LLValue* src, const char* name) -{ - llvm::LoadInst* ld = gIR->ir->CreateLoad(src, name); - ld->setVolatile(true); - return ld; +LLValue *DtoVolatileLoad(LLValue *src, const char *name) { + llvm::LoadInst *ld = gIR->ir->CreateLoad(src, name); + ld->setVolatile(true); + return ld; } - -void DtoStore(LLValue* src, LLValue* dst) -{ - assert(src->getType() != llvm::Type::getInt1Ty(gIR->context()) && - "Should store bools as i8 instead of i1."); - gIR->ir->CreateStore(src,dst); +void DtoStore(LLValue *src, LLValue *dst) { + assert(src->getType() != llvm::Type::getInt1Ty(gIR->context()) && + "Should store bools as i8 instead of i1."); + gIR->ir->CreateStore(src, dst); } -void DtoVolatileStore(LLValue* src, LLValue* dst) -{ - assert(src->getType() != llvm::Type::getInt1Ty(gIR->context()) && - "Should store bools as i8 instead of i1."); - gIR->ir->CreateStore(src, dst)->setVolatile(true); +void DtoVolatileStore(LLValue *src, LLValue *dst) { + assert(src->getType() != llvm::Type::getInt1Ty(gIR->context()) && + "Should store bools as i8 instead of i1."); + gIR->ir->CreateStore(src, dst)->setVolatile(true); } -void DtoStoreZextI8(LLValue* src, LLValue* dst) -{ - if (src->getType() == llvm::Type::getInt1Ty(gIR->context())) - { - llvm::Type* i8 = llvm::Type::getInt8Ty(gIR->context()); - assert(dst->getType()->getContainedType(0) == i8); - src = gIR->ir->CreateZExt(src, i8); - } - gIR->ir->CreateStore(src, dst); +void DtoStoreZextI8(LLValue *src, LLValue *dst) { + if (src->getType() == llvm::Type::getInt1Ty(gIR->context())) { + llvm::Type *i8 = llvm::Type::getInt8Ty(gIR->context()); + assert(dst->getType()->getContainedType(0) == i8); + src = gIR->ir->CreateZExt(src, i8); + } + gIR->ir->CreateStore(src, dst); } -// Like DtoStore, but the pointer is guaranteed to be aligned appropriately for the type. -void DtoAlignedStore(LLValue* src, LLValue* dst) -{ - assert(src->getType() != llvm::Type::getInt1Ty(gIR->context()) && - "Should store bools as i8 instead of i1."); - llvm::StoreInst* st = gIR->ir->CreateStore(src,dst); - st->setAlignment(getABITypeAlign(src->getType())); +// Like DtoStore, but the pointer is guaranteed to be aligned appropriately for +// the type. +void DtoAlignedStore(LLValue *src, LLValue *dst) { + assert(src->getType() != llvm::Type::getInt1Ty(gIR->context()) && + "Should store bools as i8 instead of i1."); + llvm::StoreInst *st = gIR->ir->CreateStore(src, dst); + st->setAlignment(getABITypeAlign(src->getType())); } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoBitCast(LLValue* v, LLType* t, const char* name) -{ - if (v->getType() == t) - return v; - assert(!isaStruct(t)); - return gIR->ir->CreateBitCast(v, t, name); +LLValue *DtoBitCast(LLValue *v, LLType *t, const char *name) { + if (v->getType() == t) + return v; + assert(!isaStruct(t)); + return gIR->ir->CreateBitCast(v, t, name); } -LLConstant* DtoBitCast(LLConstant* v, LLType* t) -{ - if (v->getType() == t) - return v; - return llvm::ConstantExpr::getBitCast(v, t); +LLConstant *DtoBitCast(LLConstant *v, LLType *t) { + if (v->getType() == t) + return v; + return llvm::ConstantExpr::getBitCast(v, t); } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoInsertValue(LLValue* aggr, LLValue* v, unsigned idx, const char* name) -{ - return gIR->ir->CreateInsertValue(aggr, v, idx, name); +LLValue *DtoInsertValue(LLValue *aggr, LLValue *v, unsigned idx, + const char *name) { + return gIR->ir->CreateInsertValue(aggr, v, idx, name); } -LLValue* DtoExtractValue(LLValue* aggr, unsigned idx, const char* name) -{ - return gIR->ir->CreateExtractValue(aggr, idx, name); +LLValue *DtoExtractValue(LLValue *aggr, unsigned idx, const char *name) { + return gIR->ir->CreateExtractValue(aggr, idx, name); } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoInsertElement(LLValue* vec, LLValue* v, LLValue *idx, const char* name) -{ - return gIR->ir->CreateInsertElement(vec, v, idx, name); +LLValue *DtoInsertElement(LLValue *vec, LLValue *v, LLValue *idx, + const char *name) { + return gIR->ir->CreateInsertElement(vec, v, idx, name); } -LLValue* DtoExtractElement(LLValue* vec, LLValue *idx, const char* name) -{ - return gIR->ir->CreateExtractElement(vec, idx, name); +LLValue *DtoExtractElement(LLValue *vec, LLValue *idx, const char *name) { + return gIR->ir->CreateExtractElement(vec, idx, name); } -LLValue* DtoInsertElement(LLValue* vec, LLValue* v, unsigned idx, const char* name) -{ - return DtoInsertElement(vec, v, DtoConstUint(idx), name); +LLValue *DtoInsertElement(LLValue *vec, LLValue *v, unsigned idx, + const char *name) { + return DtoInsertElement(vec, v, DtoConstUint(idx), name); } -LLValue* DtoExtractElement(LLValue* vec, unsigned idx, const char* name) -{ - return DtoExtractElement(vec, DtoConstUint(idx), name); +LLValue *DtoExtractElement(LLValue *vec, unsigned idx, const char *name) { + return DtoExtractElement(vec, DtoConstUint(idx), name); } ////////////////////////////////////////////////////////////////////////////////////////// -LLPointerType* isaPointer(LLValue* v) -{ - return llvm::dyn_cast(v->getType()); +LLPointerType *isaPointer(LLValue *v) { + return llvm::dyn_cast(v->getType()); } -LLPointerType* isaPointer(LLType* t) -{ - return llvm::dyn_cast(t); +LLPointerType *isaPointer(LLType *t) { + return llvm::dyn_cast(t); } -LLArrayType* isaArray(LLValue* v) -{ - return llvm::dyn_cast(v->getType()); +LLArrayType *isaArray(LLValue *v) { + return llvm::dyn_cast(v->getType()); } -LLArrayType* isaArray(LLType* t) -{ - return llvm::dyn_cast(t); +LLArrayType *isaArray(LLType *t) { return llvm::dyn_cast(t); } + +LLStructType *isaStruct(LLValue *v) { + return llvm::dyn_cast(v->getType()); } -LLStructType* isaStruct(LLValue* v) -{ - return llvm::dyn_cast(v->getType()); +LLStructType *isaStruct(LLType *t) { return llvm::dyn_cast(t); } + +LLFunctionType *isaFunction(LLValue *v) { + return llvm::dyn_cast(v->getType()); } -LLStructType* isaStruct(LLType* t) -{ - return llvm::dyn_cast(t); +LLFunctionType *isaFunction(LLType *t) { + return llvm::dyn_cast(t); } -LLFunctionType* isaFunction(LLValue* v) -{ - return llvm::dyn_cast(v->getType()); +LLConstant *isaConstant(LLValue *v) { + return llvm::dyn_cast(v); } -LLFunctionType* isaFunction(LLType* t) -{ - return llvm::dyn_cast(t); +llvm::ConstantInt *isaConstantInt(LLValue *v) { + return llvm::dyn_cast(v); } -LLConstant* isaConstant(LLValue* v) -{ - return llvm::dyn_cast(v); +llvm::Argument *isaArgument(LLValue *v) { + return llvm::dyn_cast(v); } -llvm::ConstantInt* isaConstantInt(LLValue* v) -{ - return llvm::dyn_cast(v); -} - -llvm::Argument* isaArgument(LLValue* v) -{ - return llvm::dyn_cast(v); -} - -llvm::GlobalVariable* isaGlobalVar(LLValue* v) -{ - return llvm::dyn_cast(v); +llvm::GlobalVariable *isaGlobalVar(LLValue *v) { + return llvm::dyn_cast(v); } ////////////////////////////////////////////////////////////////////////////////////////// -LLPointerType* getPtrToType(LLType* t) -{ - if (t == LLType::getVoidTy(gIR->context())) - t = LLType::getInt8Ty(gIR->context()); - return LLPointerType::get(t, 0); +LLPointerType *getPtrToType(LLType *t) { + if (t == LLType::getVoidTy(gIR->context())) + t = LLType::getInt8Ty(gIR->context()); + return LLPointerType::get(t, 0); } -LLPointerType* getVoidPtrType() -{ - return getPtrToType(LLType::getInt8Ty(gIR->context())); +LLPointerType *getVoidPtrType() { + return getPtrToType(LLType::getInt8Ty(gIR->context())); } -llvm::ConstantPointerNull* getNullPtr(LLType* t) -{ - LLPointerType* pt = llvm::cast(t); - return llvm::ConstantPointerNull::get(pt); +llvm::ConstantPointerNull *getNullPtr(LLType *t) { + LLPointerType *pt = llvm::cast(t); + return llvm::ConstantPointerNull::get(pt); } -LLConstant* getNullValue(LLType* t) -{ - return LLConstant::getNullValue(t); +LLConstant *getNullValue(LLType *t) { return LLConstant::getNullValue(t); } + +////////////////////////////////////////////////////////////////////////////////////////// + +size_t getTypeBitSize(LLType *t) { return gDataLayout->getTypeSizeInBits(t); } + +size_t getTypeStoreSize(LLType *t) { return gDataLayout->getTypeStoreSize(t); } + +size_t getTypePaddedSize(LLType *t) { + size_t sz = gDataLayout->getTypeAllocSize(t); + // Logger::cout() << "abi type size of: " << *t << " == " << sz << '\n'; + return sz; +} + +size_t getTypeAllocSize(LLType *t) { return gDataLayout->getTypeAllocSize(t); } + +unsigned int getABITypeAlign(LLType *t) { + return gDataLayout->getABITypeAlignment(t); } ////////////////////////////////////////////////////////////////////////////////////////// -size_t getTypeBitSize(LLType* t) -{ - return gDataLayout->getTypeSizeInBits(t); -} +LLStructType *DtoMutexType() { + if (gIR->mutexType) + return gIR->mutexType; -size_t getTypeStoreSize(LLType* t) -{ - return gDataLayout->getTypeStoreSize(t); -} + // The structures defined here must be the same as in + // druntime/src/rt/critical.c -size_t getTypePaddedSize(LLType* t) -{ - size_t sz = gDataLayout->getTypeAllocSize(t); - //Logger::cout() << "abi type size of: " << *t << " == " << sz << '\n'; - return sz; -} + // Windows + if (global.params.targetTriple.isOSWindows()) { + llvm::Type *VoidPtrTy = llvm::Type::getInt8PtrTy(gIR->context()); + llvm::Type *Int32Ty = llvm::Type::getInt32Ty(gIR->context()); -size_t getTypeAllocSize(LLType* t) -{ - return gDataLayout->getTypeAllocSize(t); -} - -unsigned int getABITypeAlign(LLType* t) -{ - return gDataLayout->getABITypeAlignment(t); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -LLStructType* DtoMutexType() -{ - if (gIR->mutexType) - return gIR->mutexType; - - // The structures defined here must be the same as in druntime/src/rt/critical.c - - // Windows - if (global.params.targetTriple.isOSWindows()) - { - llvm::Type *VoidPtrTy = llvm::Type::getInt8PtrTy(gIR->context()); - llvm::Type *Int32Ty = llvm::Type::getInt32Ty(gIR->context()); - - // Build RTL_CRITICAL_SECTION; size is 24 (32bit) or 40 (64bit) - LLType *rtl_types[] = { - VoidPtrTy, // Pointer to DebugInfo - Int32Ty, // LockCount - Int32Ty, // RecursionCount - VoidPtrTy, // Handle of OwningThread - VoidPtrTy, // Handle of LockSemaphore - VoidPtrTy // SpinCount - }; - LLStructType* rtl = LLStructType::create(gIR->context(), rtl_types, "RTL_CRITICAL_SECTION"); - - // Build D_CRITICAL_SECTION; size is 28 (32bit) or 48 (64bit) - LLStructType *mutex = LLStructType::create(gIR->context(), "D_CRITICAL_SECTION"); - LLType *types[] = { getPtrToType(mutex), rtl }; - mutex->setBody(types); - - // Cache type - gIR->mutexType = mutex; - - return mutex; - } - - // FreeBSD - else if (global.params.targetTriple.getOS() == llvm::Triple::FreeBSD) { - // Just a pointer - return LLStructType::get(gIR->context(), DtoSize_t()); - } - - // pthread_fastlock - LLType *types2[] = { - DtoSize_t(), - LLType::getInt32Ty(gIR->context()) + // Build RTL_CRITICAL_SECTION; size is 24 (32bit) or 40 (64bit) + LLType *rtl_types[] = { + VoidPtrTy, // Pointer to DebugInfo + Int32Ty, // LockCount + Int32Ty, // RecursionCount + VoidPtrTy, // Handle of OwningThread + VoidPtrTy, // Handle of LockSemaphore + VoidPtrTy // SpinCount }; - LLStructType* fastlock = LLStructType::get(gIR->context(), types2, false); + LLStructType *rtl = + LLStructType::create(gIR->context(), rtl_types, "RTL_CRITICAL_SECTION"); - // pthread_mutex - LLType *types1[] = { - LLType::getInt32Ty(gIR->context()), - LLType::getInt32Ty(gIR->context()), - getVoidPtrType(), - LLType::getInt32Ty(gIR->context()), - fastlock - }; - LLStructType* pmutex = LLStructType::get(gIR->context(), types1, false); - - // D_CRITICAL_SECTION - LLStructType* mutex = LLStructType::create(gIR->context(), "D_CRITICAL_SECTION"); - LLType *types[] = { getPtrToType(mutex), pmutex }; + // Build D_CRITICAL_SECTION; size is 28 (32bit) or 48 (64bit) + LLStructType *mutex = + LLStructType::create(gIR->context(), "D_CRITICAL_SECTION"); + LLType *types[] = {getPtrToType(mutex), rtl}; mutex->setBody(types); // Cache type gIR->mutexType = mutex; - return pmutex; + return mutex; + } + + // FreeBSD + else if (global.params.targetTriple.getOS() == llvm::Triple::FreeBSD) { + // Just a pointer + return LLStructType::get(gIR->context(), DtoSize_t()); + } + + // pthread_fastlock + LLType *types2[] = {DtoSize_t(), LLType::getInt32Ty(gIR->context())}; + LLStructType *fastlock = LLStructType::get(gIR->context(), types2, false); + + // pthread_mutex + LLType *types1[] = {LLType::getInt32Ty(gIR->context()), + LLType::getInt32Ty(gIR->context()), getVoidPtrType(), + LLType::getInt32Ty(gIR->context()), fastlock}; + LLStructType *pmutex = LLStructType::get(gIR->context(), types1, false); + + // D_CRITICAL_SECTION + LLStructType *mutex = + LLStructType::create(gIR->context(), "D_CRITICAL_SECTION"); + LLType *types[] = {getPtrToType(mutex), pmutex}; + mutex->setBody(types); + + // Cache type + gIR->mutexType = mutex; + + return pmutex; } ////////////////////////////////////////////////////////////////////////////////////////// -LLStructType* DtoModuleReferenceType() -{ - if (gIR->moduleRefType) - return gIR->moduleRefType; +LLStructType *DtoModuleReferenceType() { + if (gIR->moduleRefType) + return gIR->moduleRefType; - // this is a recursive type so start out with a struct without body - LLStructType* st = LLStructType::create(gIR->context(), "ModuleReference"); + // this is a recursive type so start out with a struct without body + LLStructType *st = LLStructType::create(gIR->context(), "ModuleReference"); - // add members - LLType *types[] = { - getPtrToType(st), - DtoType(Module::moduleinfo->type->pointerTo()) - }; + // add members + LLType *types[] = {getPtrToType(st), + DtoType(Module::moduleinfo->type->pointerTo())}; - // resolve type - st->setBody(types); + // resolve type + st->setBody(types); - // done - gIR->moduleRefType = st; - return st; + // done + gIR->moduleRefType = st; + return st; } ////////////////////////////////////////////////////////////////////////////////////////// -LLValue* DtoAggrPair(LLType* type, LLValue* V1, LLValue* V2, const char* name) -{ - LLValue* res = llvm::UndefValue::get(type); - res = gIR->ir->CreateInsertValue(res, V1, 0); - return gIR->ir->CreateInsertValue(res, V2, 1, name); +LLValue *DtoAggrPair(LLType *type, LLValue *V1, LLValue *V2, const char *name) { + LLValue *res = llvm::UndefValue::get(type); + res = gIR->ir->CreateInsertValue(res, V1, 0); + return gIR->ir->CreateInsertValue(res, V2, 1, name); } -LLValue* DtoAggrPair(LLValue* V1, LLValue* V2, const char* name) -{ - LLType *types[] = { V1->getType(), V2->getType() }; - LLType *t = LLStructType::get(gIR->context(), types, false); - return DtoAggrPair(t, V1, V2, name); +LLValue *DtoAggrPair(LLValue *V1, LLValue *V2, const char *name) { + LLType *types[] = {V1->getType(), V2->getType()}; + LLType *t = LLStructType::get(gIR->context(), types, false); + return DtoAggrPair(t, V1, V2, name); } -LLValue* DtoAggrPaint(LLValue* aggr, LLType* as) -{ - if (aggr->getType() == as) - return aggr; +LLValue *DtoAggrPaint(LLValue *aggr, LLType *as) { + if (aggr->getType() == as) + return aggr; - LLValue* res = llvm::UndefValue::get(as); + LLValue *res = llvm::UndefValue::get(as); - LLValue* V = gIR->ir->CreateExtractValue(aggr, 0); - V = DtoBitCast(V, as->getContainedType(0)); - res = gIR->ir->CreateInsertValue(res, V, 0); + LLValue *V = gIR->ir->CreateExtractValue(aggr, 0); + V = DtoBitCast(V, as->getContainedType(0)); + res = gIR->ir->CreateInsertValue(res, V, 0); - V = gIR->ir->CreateExtractValue(aggr, 1); - V = DtoBitCast(V, as->getContainedType(1)); - return gIR->ir->CreateInsertValue(res, V, 1); + V = gIR->ir->CreateExtractValue(aggr, 1); + V = DtoBitCast(V, as->getContainedType(1)); + return gIR->ir->CreateInsertValue(res, V, 1); } diff --git a/gen/tollvm.h b/gen/tollvm.h index 99a8ffa958..9d8fd7e875 100644 --- a/gen/tollvm.h +++ b/gen/tollvm.h @@ -28,115 +28,122 @@ /* The function takes a d type and returns an appropriate llvm type. * - * Notice that the function does not support function types with context arguments. + * Notice that the function does not support function types with context + * arguments. * DtoTypeFunction(FuncDeclaration*) is to be used instead. */ -LLType* DtoType(Type* t); +LLType *DtoType(Type *t); // Uses DtoType(), but promotes i1 and void to i8. -LLType* DtoMemType(Type* t); +LLType *DtoMemType(Type *t); // Returns a pointer to the type returned by DtoMemType(t). -LLPointerType* DtoPtrToType(Type* t); - -LLType* voidToI8(LLType* t); -LLType* i1ToI8(LLType* t); - +LLPointerType *DtoPtrToType(Type *t); +LLType *voidToI8(LLType *t); +LLType *i1ToI8(LLType *t); // returns true if the type must be passed by pointer -bool DtoIsPassedByRef(Type* type); +bool DtoIsPassedByRef(Type *type); // returns true if the return value of the call expression // is passed in a register bool DtoIsReturnInArg(CallExp *ce); // should argument be zero or sign extended -LLAttribute DtoShouldExtend(Type* type); +LLAttribute DtoShouldExtend(Type *type); // tuple helper // takes a arguments list and makes a struct type out of them -//LLType* DtoStructTypeFromArguments(Arguments* arguments); +// LLType* DtoStructTypeFromArguments(Arguments* arguments); // delegate helpers -LLValue* DtoDelegateEquals(TOK op, LLValue* lhs, LLValue* rhs); +LLValue *DtoDelegateEquals(TOK op, LLValue *lhs, LLValue *rhs); // Returns the LLVM linkage to use for the definition of the given symbol, // based on whether it is a template or not. typedef std::pair LinkageWithCOMDAT; -LinkageWithCOMDAT DtoLinkage(Dsymbol* sym); +LinkageWithCOMDAT DtoLinkage(Dsymbol *sym); // some types -LLIntegerType* DtoSize_t(); -LLStructType* DtoMutexType(); -LLStructType* DtoModuleReferenceType(); +LLIntegerType *DtoSize_t(); +LLStructType *DtoMutexType(); +LLStructType *DtoModuleReferenceType(); // getelementptr helpers -LLValue* DtoGEP1(LLValue* ptr, LLValue* i0, const char* name = "", llvm::BasicBlock* bb = NULL); -LLValue* DtoGEP(LLValue* ptr, LLValue* i0, LLValue* i1, const char* name = "", llvm::BasicBlock* bb = NULL); +LLValue *DtoGEP1(LLValue *ptr, LLValue *i0, const char *name = "", + llvm::BasicBlock *bb = NULL); +LLValue *DtoGEP(LLValue *ptr, LLValue *i0, LLValue *i1, const char *name = "", + llvm::BasicBlock *bb = NULL); -LLValue* DtoGEPi1(LLValue* ptr, unsigned i0, const char* name = "", llvm::BasicBlock* bb = NULL); -LLValue* DtoGEPi(LLValue* ptr, unsigned i0, unsigned i1, const char* name = "", llvm::BasicBlock* bb = NULL); -LLConstant* DtoGEPi(LLConstant* ptr, unsigned i0, unsigned i1); +LLValue *DtoGEPi1(LLValue *ptr, unsigned i0, const char *name = "", + llvm::BasicBlock *bb = NULL); +LLValue *DtoGEPi(LLValue *ptr, unsigned i0, unsigned i1, const char *name = "", + llvm::BasicBlock *bb = NULL); +LLConstant *DtoGEPi(LLConstant *ptr, unsigned i0, unsigned i1); // to constant helpers -LLConstantInt* DtoConstSize_t(uint64_t); -LLConstantInt* DtoConstUint(unsigned i); -LLConstantInt* DtoConstInt(int i); -LLConstantInt* DtoConstUbyte(unsigned char i); -LLConstant* DtoConstFP(Type* t, longdouble value); +LLConstantInt *DtoConstSize_t(uint64_t); +LLConstantInt *DtoConstUint(unsigned i); +LLConstantInt *DtoConstInt(int i); +LLConstantInt *DtoConstUbyte(unsigned char i); +LLConstant *DtoConstFP(Type *t, longdouble value); -LLConstant* DtoConstString(const char*); -LLConstant* DtoConstBool(bool); +LLConstant *DtoConstString(const char *); +LLConstant *DtoConstBool(bool); // llvm wrappers -LLValue* DtoLoad(LLValue* src, const char* name = ""); -LLValue* DtoVolatileLoad(LLValue* src, const char* name = ""); -LLValue* DtoAlignedLoad(LLValue* src, const char* name = ""); -void DtoStore(LLValue* src, LLValue* dst); -void DtoVolatileStore(LLValue* src, LLValue* dst); -void DtoStoreZextI8(LLValue* src, LLValue* dst); -void DtoAlignedStore(LLValue* src, LLValue* dst); -LLValue* DtoBitCast(LLValue* v, LLType* t, const char* name = ""); -LLConstant* DtoBitCast(LLConstant* v, LLType* t); -LLValue* DtoInsertValue(LLValue* aggr, LLValue* v, unsigned idx, const char* name = ""); -LLValue* DtoExtractValue(LLValue* aggr, unsigned idx, const char* name = ""); -LLValue* DtoInsertElement(LLValue* vec, LLValue* v, LLValue *idx, const char* name = ""); -LLValue* DtoExtractElement(LLValue* vec, LLValue *idx, const char* name = ""); -LLValue* DtoInsertElement(LLValue* vec, LLValue* v, unsigned idx, const char* name = ""); -LLValue* DtoExtractElement(LLValue* vec, unsigned idx, const char* name = ""); +LLValue *DtoLoad(LLValue *src, const char *name = ""); +LLValue *DtoVolatileLoad(LLValue *src, const char *name = ""); +LLValue *DtoAlignedLoad(LLValue *src, const char *name = ""); +void DtoStore(LLValue *src, LLValue *dst); +void DtoVolatileStore(LLValue *src, LLValue *dst); +void DtoStoreZextI8(LLValue *src, LLValue *dst); +void DtoAlignedStore(LLValue *src, LLValue *dst); +LLValue *DtoBitCast(LLValue *v, LLType *t, const char *name = ""); +LLConstant *DtoBitCast(LLConstant *v, LLType *t); +LLValue *DtoInsertValue(LLValue *aggr, LLValue *v, unsigned idx, + const char *name = ""); +LLValue *DtoExtractValue(LLValue *aggr, unsigned idx, const char *name = ""); +LLValue *DtoInsertElement(LLValue *vec, LLValue *v, LLValue *idx, + const char *name = ""); +LLValue *DtoExtractElement(LLValue *vec, LLValue *idx, const char *name = ""); +LLValue *DtoInsertElement(LLValue *vec, LLValue *v, unsigned idx, + const char *name = ""); +LLValue *DtoExtractElement(LLValue *vec, unsigned idx, const char *name = ""); // llvm::dyn_cast wrappers -LLPointerType* isaPointer(LLValue* v); -LLPointerType* isaPointer(LLType* t); -LLArrayType* isaArray(LLValue* v); -LLArrayType* isaArray(LLType* t); -LLStructType* isaStruct(LLValue* v); -LLStructType* isaStruct(LLType* t); -LLFunctionType* isaFunction(LLValue* v); -LLFunctionType* isaFunction(LLType* t); -LLConstant* isaConstant(LLValue* v); -LLConstantInt* isaConstantInt(LLValue* v); -llvm::Argument* isaArgument(LLValue* v); -LLGlobalVariable* isaGlobalVar(LLValue* v); +LLPointerType *isaPointer(LLValue *v); +LLPointerType *isaPointer(LLType *t); +LLArrayType *isaArray(LLValue *v); +LLArrayType *isaArray(LLType *t); +LLStructType *isaStruct(LLValue *v); +LLStructType *isaStruct(LLType *t); +LLFunctionType *isaFunction(LLValue *v); +LLFunctionType *isaFunction(LLType *t); +LLConstant *isaConstant(LLValue *v); +LLConstantInt *isaConstantInt(LLValue *v); +llvm::Argument *isaArgument(LLValue *v); +LLGlobalVariable *isaGlobalVar(LLValue *v); // llvm::T::get(...) wrappers -LLPointerType* getPtrToType(LLType* t); -LLPointerType* getVoidPtrType(); -llvm::ConstantPointerNull* getNullPtr(LLType* t); -LLConstant* getNullValue(LLType* t); +LLPointerType *getPtrToType(LLType *t); +LLPointerType *getVoidPtrType(); +llvm::ConstantPointerNull *getNullPtr(LLType *t); +LLConstant *getNullValue(LLType *t); // type sizes -size_t getTypeBitSize(LLType* t); -size_t getTypeStoreSize(LLType* t); -size_t getTypePaddedSize(LLType* t); -size_t getTypeAllocSize(LLType* t); +size_t getTypeBitSize(LLType *t); +size_t getTypeStoreSize(LLType *t); +size_t getTypePaddedSize(LLType *t); +size_t getTypeAllocSize(LLType *t); // type alignments -unsigned int getABITypeAlign(LLType* t); +unsigned int getABITypeAlign(LLType *t); // pair type helpers -LLValue* DtoAggrPair(LLType* type, LLValue* V1, LLValue* V2, const char* name = ""); -LLValue* DtoAggrPair(LLValue* V1, LLValue* V2, const char* name = ""); -LLValue* DtoAggrPaint(LLValue* aggr, LLType* as); +LLValue *DtoAggrPair(LLType *type, LLValue *V1, LLValue *V2, + const char *name = ""); +LLValue *DtoAggrPair(LLValue *V1, LLValue *V2, const char *name = ""); +LLValue *DtoAggrPaint(LLValue *aggr, LLType *as); /** * Generates a call to llvm.memset.i32 (or i64 depending on architecture). @@ -144,14 +151,14 @@ LLValue* DtoAggrPaint(LLValue* aggr, LLType* as); * @param val The value to set. * @param nbytes Number of bytes to overwrite. */ -void DtoMemSet(LLValue* dst, LLValue* val, LLValue* nbytes); +void DtoMemSet(LLValue *dst, LLValue *val, LLValue *nbytes); /** * Generates a call to llvm.memset.i32 (or i64 depending on architecture). * @param dst Destination memory. * @param nbytes Number of bytes to overwrite. */ -void DtoMemSetZero(LLValue* dst, LLValue* nbytes); +void DtoMemSetZero(LLValue *dst, LLValue *nbytes); /** * Generates a call to llvm.memcpy.i32 (or i64 depending on architecture). @@ -160,24 +167,26 @@ void DtoMemSetZero(LLValue* dst, LLValue* nbytes); * @param nbytes Number of bytes to copy. * @param align The minimum alignment of the source and destination memory. */ -void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes, unsigned align = 1); +void DtoMemCpy(LLValue *dst, LLValue *src, LLValue *nbytes, unsigned align = 1); /** * Generates a call to C memcmp. */ -LLValue* DtoMemCmp(LLValue* lhs, LLValue* rhs, LLValue* nbytes); +LLValue *DtoMemCmp(LLValue *lhs, LLValue *rhs, LLValue *nbytes); /** - * The same as DtoMemSetZero but figures out the size itself by "dereferencing" the v pointer once. + * The same as DtoMemSetZero but figures out the size itself by "dereferencing" + * the v pointer once. * @param v Destination memory. */ -void DtoAggrZeroInit(LLValue* v); +void DtoAggrZeroInit(LLValue *v); /** - * The same as DtoMemCpy but figures out the size itself by "dereferencing" dst the pointer once. + * The same as DtoMemCpy but figures out the size itself by "dereferencing" dst + * the pointer once. * @param dst Destination memory. * @param src Source memory. */ -void DtoAggrCopy(LLValue* dst, LLValue* src); +void DtoAggrCopy(LLValue *dst, LLValue *src); #endif // LDC_GEN_TOLLVM_H diff --git a/gen/typeinf.h b/gen/typeinf.h index c1eae400d2..7c7baea8b7 100644 --- a/gen/typeinf.h +++ b/gen/typeinf.h @@ -16,8 +16,8 @@ #define LDC_GEN_TYPEINF_H class TypeInfoDeclaration; -void DtoResolveTypeInfo(TypeInfoDeclaration* tid); +void DtoResolveTypeInfo(TypeInfoDeclaration *tid); TypeInfoDeclaration *getOrCreateTypeInfoDeclaration(Type *t, Scope *sc); -void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState* p); +void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p); #endif diff --git a/gen/typinf.cpp b/gen/typinf.cpp index 2e5d738c89..f7c0105bea 100644 --- a/gen/typinf.cpp +++ b/gen/typinf.cpp @@ -54,91 +54,93 @@ static bool builtinTypeInfo(Type *t); FuncDeclaration *search_toString(StructDeclaration *sd); -namespace -{ -TypeInfoDeclaration *createUnqualified(Type *t) -{ - switch (t->ty) - { - case Tpointer: return TypeInfoPointerDeclaration::create(t); - case Tarray: return TypeInfoArrayDeclaration::create(t); - case Tsarray: return TypeInfoStaticArrayDeclaration::create(t); - case Taarray: return TypeInfoAssociativeArrayDeclaration::create(t); - case Tstruct: return TypeInfoStructDeclaration::create(t); - case Tvector: return TypeInfoVectorDeclaration::create(t); - case Tenum: return TypeInfoEnumDeclaration::create(t); - case Tfunction: return TypeInfoFunctionDeclaration::create(t); - case Tdelegate: return TypeInfoDelegateDeclaration::create(t); - case Ttuple: return TypeInfoTupleDeclaration::create(t); - case Tclass: - if (((TypeClass *)t)->sym->isInterfaceDeclaration()) - return TypeInfoInterfaceDeclaration::create(t); - else - return TypeInfoClassDeclaration::create(t); - default: - return TypeInfoDeclaration::create(t, 0); - } +namespace { +TypeInfoDeclaration *createUnqualified(Type *t) { + switch (t->ty) { + case Tpointer: + return TypeInfoPointerDeclaration::create(t); + case Tarray: + return TypeInfoArrayDeclaration::create(t); + case Tsarray: + return TypeInfoStaticArrayDeclaration::create(t); + case Taarray: + return TypeInfoAssociativeArrayDeclaration::create(t); + case Tstruct: + return TypeInfoStructDeclaration::create(t); + case Tvector: + return TypeInfoVectorDeclaration::create(t); + case Tenum: + return TypeInfoEnumDeclaration::create(t); + case Tfunction: + return TypeInfoFunctionDeclaration::create(t); + case Tdelegate: + return TypeInfoDelegateDeclaration::create(t); + case Ttuple: + return TypeInfoTupleDeclaration::create(t); + case Tclass: + if (((TypeClass *)t)->sym->isInterfaceDeclaration()) + return TypeInfoInterfaceDeclaration::create(t); + else + return TypeInfoClassDeclaration::create(t); + default: + return TypeInfoDeclaration::create(t, 0); + } } } +TypeInfoDeclaration *getOrCreateTypeInfoDeclaration(Type *torig, Scope *sc) { + IF_LOG Logger::println("Type::getTypeInfo(): %s", torig->toChars()); + LOG_SCOPE -TypeInfoDeclaration *getOrCreateTypeInfoDeclaration(Type *torig, Scope *sc) -{ - IF_LOG Logger::println("Type::getTypeInfo(): %s", torig->toChars()); - LOG_SCOPE + if (!Type::dtypeinfo) { + torig->error(Loc(), "TypeInfo not found. object.d may be incorrectly " + "installed or corrupt, compile with -v switch"); + fatal(); + } - if (!Type::dtypeinfo) - { - torig->error(Loc(), "TypeInfo not found. object.d may be incorrectly installed or corrupt, compile with -v switch"); - fatal(); + Type *t = torig->merge2(); // do this since not all Type's are merge'd + if (!t->vtinfo) { + if (t->isShared()) // does both 'shared' and 'shared const' + t->vtinfo = new TypeInfoSharedDeclaration(t); + else if (t->isConst()) + t->vtinfo = new TypeInfoConstDeclaration(t); + else if (t->isImmutable()) + t->vtinfo = new TypeInfoInvariantDeclaration(t); + else if (t->isWild()) + t->vtinfo = new TypeInfoWildDeclaration(t); + else + t->vtinfo = createUnqualified(t); + assert(t->vtinfo); + torig->vtinfo = t->vtinfo; + + /* If this has a custom implementation in std/typeinfo, then + * do not generate a COMDAT for it. + */ + if (!builtinTypeInfo(t)) { // Generate COMDAT + if (sc) // if in semantic() pass + { + // Find module that will go all the way to an object file + Module *m = sc->module->importedFrom; + m->members->push(t->vtinfo); + + semanticTypeInfo(sc, t); + } else // if in obj generation pass + { + Declaration_codegen(t->vtinfo); + } } - - Type *t = torig->merge2(); // do this since not all Type's are merge'd - if (!t->vtinfo) - { - if (t->isShared()) // does both 'shared' and 'shared const' - t->vtinfo = new TypeInfoSharedDeclaration(t); - else if (t->isConst()) - t->vtinfo = new TypeInfoConstDeclaration(t); - else if (t->isImmutable()) - t->vtinfo = new TypeInfoInvariantDeclaration(t); - else if (t->isWild()) - t->vtinfo = new TypeInfoWildDeclaration(t); - else - t->vtinfo = createUnqualified(t); - assert(t->vtinfo); - torig->vtinfo = t->vtinfo; - - /* If this has a custom implementation in std/typeinfo, then - * do not generate a COMDAT for it. - */ - if (!builtinTypeInfo(t)) - { // Generate COMDAT - if (sc) // if in semantic() pass - { - // Find module that will go all the way to an object file - Module *m = sc->module->importedFrom; - m->members->push(t->vtinfo); - - semanticTypeInfo(sc, t); - } - else // if in obj generation pass - { - Declaration_codegen(t->vtinfo); - } - } - } - if (!torig->vtinfo) - torig->vtinfo = t->vtinfo; // Types aren't merged, but we can share the vtinfo's - assert(torig->vtinfo); - return torig->vtinfo; + } + if (!torig->vtinfo) + torig->vtinfo = + t->vtinfo; // Types aren't merged, but we can share the vtinfo's + assert(torig->vtinfo); + return torig->vtinfo; } -Type *getTypeInfoType(Type *t, Scope *sc) -{ - assert(t->ty != Terror); - getOrCreateTypeInfoDeclaration(t, sc); - return t->vtinfo->type; +Type *getTypeInfoType(Type *t, Scope *sc) { + assert(t->ty != Terror); + getOrCreateTypeInfoDeclaration(t, sc); + return t->vtinfo->type; } /* ========================================================================= */ @@ -147,27 +149,25 @@ Type *getTypeInfoType(Type *t, Scope *sc) * because then the compiler doesn't need to build one. */ -static bool builtinTypeInfo(Type *t) -{ +static bool builtinTypeInfo(Type *t) { #if 0 // FIXME if I enable for Tclass, the way LDC does typeinfo will cause a // bunch of linker errors to missing class typeinfo definitions. if (t->isTypeBasic() || t->ty == Tclass) return !t->mod; #else - if (t->isTypeBasic()) - return !t->mod; + if (t->isTypeBasic()) + return !t->mod; #endif - if (t->ty == Tarray) - { - Type *next = t->nextOf(); - return !t->mod && ((next->isTypeBasic() != NULL && !next->mod) || - // strings are so common, make them builtin - (next->ty == Tchar && next->mod == MODimmutable) || - (next->ty == Tchar && next->mod == MODconst)); - } - return false; + if (t->ty == Tarray) { + Type *next = t->nextOf(); + return !t->mod && ((next->isTypeBasic() != NULL && !next->mod) || + // strings are so common, make them builtin + (next->ty == Tchar && next->mod == MODimmutable) || + (next->ty == Tchar && next->mod == MODconst)); + } + return false; } /* ========================================================================= */ @@ -177,582 +177,592 @@ static bool builtinTypeInfo(Type *t) // (wut?) ////////////////////////////////////////////////////////////////////////////// -static void emitTypeMetadata(TypeInfoDeclaration *tid) -{ - // We don't want to generate metadata for non-concrete types (such as tuple - // types, slice types, typeof(expr), etc.), void and function types (without - // an indirection), as there must be a valid LLVM undef value of that type. - // As those types cannot appear as LLVM values, they are not interesting for - // the optimizer passes anyway. - Type* t = tid->tinfo->toBasetype(); - if (t->ty < Terror && t->ty != Tvoid && t->ty != Tfunction && t->ty != Tident) { - // Add some metadata for use by optimization passes. - std::string metaname(TD_PREFIX); - metaname += mangle(tid); - llvm::NamedMDNode* meta = gIR->module.getNamedMetadata(metaname); +static void emitTypeMetadata(TypeInfoDeclaration *tid) { + // We don't want to generate metadata for non-concrete types (such as tuple + // types, slice types, typeof(expr), etc.), void and function types (without + // an indirection), as there must be a valid LLVM undef value of that type. + // As those types cannot appear as LLVM values, they are not interesting for + // the optimizer passes anyway. + Type *t = tid->tinfo->toBasetype(); + if (t->ty < Terror && t->ty != Tvoid && t->ty != Tfunction && + t->ty != Tident) { + // Add some metadata for use by optimization passes. + std::string metaname(TD_PREFIX); + metaname += mangle(tid); + llvm::NamedMDNode *meta = gIR->module.getNamedMetadata(metaname); - if (!meta) { - // Construct the fields + if (!meta) { +// Construct the fields #if LDC_LLVM_VER >= 306 - llvm::Metadata* mdVals[TD_NumFields]; - mdVals[TD_TypeInfo] = llvm::ValueAsMetadata::get(getIrGlobal(tid)->value); - mdVals[TD_Type] = llvm::ConstantAsMetadata::get(llvm::UndefValue::get(DtoType(tid->tinfo))); + llvm::Metadata *mdVals[TD_NumFields]; + mdVals[TD_TypeInfo] = llvm::ValueAsMetadata::get(getIrGlobal(tid)->value); + mdVals[TD_Type] = llvm::ConstantAsMetadata::get( + llvm::UndefValue::get(DtoType(tid->tinfo))); #else - MDNodeField* mdVals[TD_NumFields]; - mdVals[TD_TypeInfo] = llvm::cast(getIrGlobal(tid)->value); - mdVals[TD_Type] = llvm::UndefValue::get(DtoType(tid->tinfo)); + MDNodeField *mdVals[TD_NumFields]; + mdVals[TD_TypeInfo] = llvm::cast(getIrGlobal(tid)->value); + mdVals[TD_Type] = llvm::UndefValue::get(DtoType(tid->tinfo)); #endif - // Construct the metadata and insert it into the module. - llvm::NamedMDNode* node = gIR->module.getOrInsertNamedMetadata(metaname); - node->addOperand(llvm::MDNode::get(gIR->context(), - llvm::makeArrayRef(mdVals, TD_NumFields))); - } + // Construct the metadata and insert it into the module. + llvm::NamedMDNode *node = gIR->module.getOrInsertNamedMetadata(metaname); + node->addOperand(llvm::MDNode::get( + gIR->context(), llvm::makeArrayRef(mdVals, TD_NumFields))); } + } } -void DtoResolveTypeInfo(TypeInfoDeclaration* tid) -{ - if (tid->ir.isResolved()) return; - tid->ir.setResolved(); +void DtoResolveTypeInfo(TypeInfoDeclaration *tid) { + if (tid->ir.isResolved()) + return; + tid->ir.setResolved(); - // TypeInfo instances (except ClassInfo ones) are always emitted as weak - // symbols when they are used. We call semanticTypeInfo() to make sure - // that the type (e.g. for structs) is semantic3'd and codegen() does not - // skip it on grounds of being speculative, as DtoResolveTypeInfo() means - // that we actually need the value somewhere else in codegen. - // TODO: DMD does not seem to call semanticTypeInfo() from the glue layer, - // so there might be a structural issue somewhere. - semanticTypeInfo(NULL, tid->tinfo); - Declaration_codegen(tid); + // TypeInfo instances (except ClassInfo ones) are always emitted as weak + // symbols when they are used. We call semanticTypeInfo() to make sure + // that the type (e.g. for structs) is semantic3'd and codegen() does not + // skip it on grounds of being speculative, as DtoResolveTypeInfo() means + // that we actually need the value somewhere else in codegen. + // TODO: DMD does not seem to call semanticTypeInfo() from the glue layer, + // so there might be a structural issue somewhere. + semanticTypeInfo(NULL, tid->tinfo); + Declaration_codegen(tid); } /* ========================================================================= */ -class LLVMDefineVisitor : public Visitor -{ +class LLVMDefineVisitor : public Visitor { public: - // Import all functions from class Visitor - using Visitor::visit; + // Import all functions from class Visitor + using Visitor::visit; - /* ========================================================================= */ + /* ========================================================================= + */ - void visit(TypeInfoDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - RTTIBuilder b(Type::dtypeinfo); - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoEnumDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoEnumDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - RTTIBuilder b(Type::typeinfoenum); - - assert(decl->tinfo->ty == Tenum); - TypeEnum *tc = static_cast(decl->tinfo); - EnumDeclaration *sd = tc->sym; - - // TypeInfo base - b.push_typeinfo(sd->memtype); - - // char[] name - b.push_string(sd->toPrettyChars()); - - // void[] init - // emit void[] with the default initialier, the array is null if the default - // initializer is zero - if (!sd->members || decl->tinfo->isZeroInit(decl->loc)) - { - b.push_null_void_array(); - } - // otherwise emit a void[] with the default initializer - else - { - Type *memtype = sd->memtype; - LLType *memty = DtoType(memtype); - LLConstant *C; - Expression *defaultval = sd->getDefaultValue(decl->loc); - if (memtype->isintegral()) - C = LLConstantInt::get(memty, defaultval->toInteger(), !isLLVMUnsigned(memtype)); - else if (memtype->isString()) - C = DtoConstString(static_cast(defaultval->toStringExp()->string)); - else if (memtype->isfloating()) - C = LLConstantFP::get(memty, defaultval->toReal()); - else - llvm_unreachable("Unsupported type"); - - b.push_void_array(C, memtype, sd); - } - - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoPointerDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoPointerDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - RTTIBuilder b(Type::typeinfopointer); - // TypeInfo base - b.push_typeinfo(decl->tinfo->nextOf()); - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoArrayDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoArrayDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - RTTIBuilder b(Type::typeinfoarray); - // TypeInfo base - b.push_typeinfo(decl->tinfo->nextOf()); - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoStaticArrayDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoStaticArrayDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - assert(decl->tinfo->ty == Tsarray); - TypeSArray *tc = static_cast(decl->tinfo); - - RTTIBuilder b(Type::typeinfostaticarray); - - // value typeinfo - b.push_typeinfo(tc->nextOf()); - - // length - b.push(DtoConstSize_t(static_cast(tc->dim->toUInteger()))); - - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoAssociativeArrayDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoAssociativeArrayDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - assert(decl->tinfo->ty == Taarray); - TypeAArray *tc = static_cast(decl->tinfo); - - RTTIBuilder b(Type::typeinfoassociativearray); - - // value typeinfo - b.push_typeinfo(tc->nextOf()); - - // key typeinfo - b.push_typeinfo(tc->index); - - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoFunctionDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoFunctionDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - RTTIBuilder b(Type::typeinfofunction); - // TypeInfo base - b.push_typeinfo(decl->tinfo->nextOf()); - // string deco - b.push_string(decl->tinfo->deco); - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoDelegateDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoDelegateDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - assert(decl->tinfo->ty == Tdelegate); - Type* ret_type = decl->tinfo->nextOf()->nextOf(); - - RTTIBuilder b(Type::typeinfodelegate); - // TypeInfo base - b.push_typeinfo(ret_type); - // string deco - b.push_string(decl->tinfo->deco); - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoStructDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoStructDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - // make sure struct is resolved - assert(decl->tinfo->ty == Tstruct); - TypeStruct *tc = static_cast(decl->tinfo); - StructDeclaration *sd = tc->sym; - - // handle opaque structs - if (!sd->members) { - RTTIBuilder b(Type::typeinfostruct); - b.finalize(getIrGlobal(decl)); - return; - } - - // can't emit typeinfo for forward declarations - if (sd->sizeok != SIZEOKdone) - { - sd->error("cannot emit TypeInfo for forward declaration"); - fatal(); - } - - DtoResolveStruct(sd); - - if (TemplateInstance *ti = sd->isInstantiated()) - { - if (!ti->needsCodegen()) - { - assert(ti->minst || sd->requestTypeInfo); - - // We won't emit ti, so emit the special member functions in here. - if (sd->xeq && sd->xeq != StructDeclaration::xerreq) { - Declaration_codegen(sd->xeq); - } - if (sd->xcmp && sd->xcmp != StructDeclaration::xerrcmp) { - Declaration_codegen(sd->xcmp); - } - if (FuncDeclaration *ftostr = search_toString(sd)) { - Declaration_codegen(ftostr); - } - if (sd->xhash) { - Declaration_codegen(sd->xhash); - } - if (sd->postblit) { - Declaration_codegen(sd->postblit); - } - if (sd->dtor) { - Declaration_codegen(sd->dtor); - } - } - } - - IrAggr* iraggr = getIrAggr(sd); - RTTIBuilder b(Type::typeinfostruct); - - // On x86_64, class TypeInfo_Struct contains 2 additional fields - // (m_arg1/m_arg2) which are used for the X86_64 System V ABI varargs - // implementation. They are not present on any other cpu/os. - unsigned expectedFields = 12; - if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) - expectedFields += 2; - if (Type::typeinfostruct->fields.dim != expectedFields) - { - error(Loc(), "Unexpected number of object.TypeInfo_Struct fields; " - "druntime version does not match compiler"); - fatal(); - } - - // char[] name - b.push_string(sd->toPrettyChars()); - - // void[] init - // The protocol is to write a null pointer for zero-initialized arrays. The - // length field is always needed for tsize(). - llvm::Constant *initPtr; - if (tc->isZeroInit(Loc())) - initPtr = getNullValue(getVoidPtrType()); - else - initPtr = iraggr->getInitSymbol(); - b.push_void_array(getTypeStoreSize(DtoType(tc)), initPtr); - - // toHash - FuncDeclaration* fd = sd->xhash; - b.push_funcptr(fd); - - // opEquals - fd = sd->xeq; - b.push_funcptr(fd); - - // opCmp - fd = sd->xcmp; - b.push_funcptr(fd); - - // toString - fd = search_toString(sd); - b.push_funcptr(fd); - - // uint m_flags; - unsigned hasptrs = tc->hasPointers() ? 1 : 0; - b.push_uint(hasptrs); - - //void function(void*) xdtor; - b.push_funcptr(sd->dtor); - - //void function(void*) xpostblit; - FuncDeclaration *xpostblit = sd->postblit; - if (xpostblit && sd->postblit->storage_class & STCdisable) - xpostblit = 0; - b.push_funcptr(xpostblit); - - //uint m_align; - b.push_uint(DtoAlignment(tc)); - - if (global.params.is64bit) - { - // TypeInfo m_arg1; - // TypeInfo m_arg2; - Type *t = sd->arg1type; - for (unsigned i = 0; i < 2; i++) - { - if (t) - { - t = t->merge(); - b.push_typeinfo(t); - } - else - b.push_null(Type::dtypeinfo->type); - - t = sd->arg2type; - } - } - - // immutable(void)* m_RTInfo; - // The cases where getRTInfo is null are not quite here, but the code is - // modelled after what DMD does. - if (sd->getRTInfo) - b.push(toConstElem(sd->getRTInfo, gIR)); - else if (!tc->hasPointers()) - b.push_size_as_vp(0); // no pointers - else - b.push_size_as_vp(1); // has pointers - - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoClassDeclaration *decl) - { - llvm_unreachable("TypeInfoClassDeclaration::llvmDefine() should not be called, " - "as a custom Dsymbol::codegen() override is used"); - } - - /* ========================================================================= */ - - void visit(TypeInfoInterfaceDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoInterfaceDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - // make sure interface is resolved - assert(decl->tinfo->ty == Tclass); - TypeClass *tc = static_cast(decl->tinfo); - DtoResolveClass(tc->sym); - - RTTIBuilder b(Type::typeinfointerface); - - // TypeInfo base - b.push_classinfo(tc->sym); - - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoTupleDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoTupleDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - // create elements array - assert(decl->tinfo->ty == Ttuple); - TypeTuple *tu = static_cast(decl->tinfo); - - size_t dim = tu->arguments->dim; - std::vector arrInits; - arrInits.reserve(dim); - - LLType* tiTy = DtoType(Type::dtypeinfo->type); - - for (size_t i = 0; i < dim; i++) - { - Parameter *arg = static_cast(tu->arguments->data[i]); - arrInits.push_back(DtoTypeInfoOf(arg->type, true)); - } - - // build array - LLArrayType* arrTy = LLArrayType::get(tiTy, dim); - LLConstant* arrC = LLConstantArray::get(arrTy, arrInits); - - RTTIBuilder b(Type::typeinfotypelist); - - // push TypeInfo[] - b.push_array(arrC, dim, Type::dtypeinfo->type, NULL); - - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoConstDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoConstDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - RTTIBuilder b(Type::typeinfoconst); - // TypeInfo base - b.push_typeinfo(decl->tinfo->mutableOf()->merge()); - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoInvariantDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoInvariantDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - RTTIBuilder b(Type::typeinfoinvariant); - // TypeInfo base - b.push_typeinfo(decl->tinfo->mutableOf()->merge()); - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoSharedDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoSharedDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - RTTIBuilder b(Type::typeinfoshared); - // TypeInfo base - b.push_typeinfo(decl->tinfo->unSharedOf()->merge()); - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoWildDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoWildDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - RTTIBuilder b(Type::typeinfowild); - // TypeInfo base - b.push_typeinfo(decl->tinfo->mutableOf()->merge()); - // finish - b.finalize(getIrGlobal(decl)); - } - - /* ========================================================================= */ - - void visit(TypeInfoVectorDeclaration *decl) - { - IF_LOG Logger::println("TypeInfoVectorDeclaration::llvmDefine() %s", decl->toChars()); - LOG_SCOPE; - - assert(decl->tinfo->ty == Tvector); - TypeVector *tv = static_cast(decl->tinfo); - - RTTIBuilder b(Type::typeinfovector); - // TypeInfo base - b.push_typeinfo(tv->basetype); - // finish - b.finalize(getIrGlobal(decl)); - } -}; - -/* ========================================================================= */ - -void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState* p) -{ - IF_LOG Logger::println("TypeInfoDeclaration::codegen(%s)", decl->toPrettyChars()); + void visit(TypeInfoDeclaration *decl) { + IF_LOG Logger::println("TypeInfoDeclaration::llvmDefine() %s", + decl->toChars()); LOG_SCOPE; - if (decl->ir.isDefined()) return; - decl->ir.setDefined(); + RTTIBuilder b(Type::dtypeinfo); + b.finalize(getIrGlobal(decl)); + } - std::string mangled(mangle(decl)); - IF_LOG { - Logger::println("type = '%s'", decl->tinfo->toChars()); - Logger::println("typeinfo mangle: %s", mangled.c_str()); + /* ========================================================================= + */ + + void visit(TypeInfoEnumDeclaration *decl) { + IF_LOG Logger::println("TypeInfoEnumDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + RTTIBuilder b(Type::typeinfoenum); + + assert(decl->tinfo->ty == Tenum); + TypeEnum *tc = static_cast(decl->tinfo); + EnumDeclaration *sd = tc->sym; + + // TypeInfo base + b.push_typeinfo(sd->memtype); + + // char[] name + b.push_string(sd->toPrettyChars()); + + // void[] init + // emit void[] with the default initialier, the array is null if the default + // initializer is zero + if (!sd->members || decl->tinfo->isZeroInit(decl->loc)) { + b.push_null_void_array(); + } + // otherwise emit a void[] with the default initializer + else { + Type *memtype = sd->memtype; + LLType *memty = DtoType(memtype); + LLConstant *C; + Expression *defaultval = sd->getDefaultValue(decl->loc); + if (memtype->isintegral()) + C = LLConstantInt::get(memty, defaultval->toInteger(), + !isLLVMUnsigned(memtype)); + else if (memtype->isString()) + C = DtoConstString( + static_cast(defaultval->toStringExp()->string)); + else if (memtype->isfloating()) + C = LLConstantFP::get(memty, defaultval->toReal()); + else + llvm_unreachable("Unsupported type"); + + b.push_void_array(C, memtype, sd); } - IrGlobal* irg = getIrGlobal(decl, true); - irg->value = gIR->module.getGlobalVariable(mangled); - if (irg->value) { - irg->type = irg->value->getType()->getContainedType(0); - assert(irg->type->isStructTy()); - } else { - if (builtinTypeInfo(decl->tinfo)) // this is a declaration of a builtin __initZ var - irg->type = Type::dtypeinfo->type->ctype->isClass()->getMemoryLLType(); - else - irg->type = LLStructType::create(gIR->context(), decl->toPrettyChars()); - irg->value = new llvm::GlobalVariable(gIR->module, irg->type, true, - llvm::GlobalValue::ExternalLinkage, NULL, mangled); + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoPointerDeclaration *decl) { + IF_LOG Logger::println("TypeInfoPointerDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + RTTIBuilder b(Type::typeinfopointer); + // TypeInfo base + b.push_typeinfo(decl->tinfo->nextOf()); + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoArrayDeclaration *decl) { + IF_LOG Logger::println("TypeInfoArrayDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + RTTIBuilder b(Type::typeinfoarray); + // TypeInfo base + b.push_typeinfo(decl->tinfo->nextOf()); + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoStaticArrayDeclaration *decl) { + IF_LOG Logger::println("TypeInfoStaticArrayDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + assert(decl->tinfo->ty == Tsarray); + TypeSArray *tc = static_cast(decl->tinfo); + + RTTIBuilder b(Type::typeinfostaticarray); + + // value typeinfo + b.push_typeinfo(tc->nextOf()); + + // length + b.push(DtoConstSize_t(static_cast(tc->dim->toUInteger()))); + + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoAssociativeArrayDeclaration *decl) { + IF_LOG Logger::println( + "TypeInfoAssociativeArrayDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + assert(decl->tinfo->ty == Taarray); + TypeAArray *tc = static_cast(decl->tinfo); + + RTTIBuilder b(Type::typeinfoassociativearray); + + // value typeinfo + b.push_typeinfo(tc->nextOf()); + + // key typeinfo + b.push_typeinfo(tc->index); + + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoFunctionDeclaration *decl) { + IF_LOG Logger::println("TypeInfoFunctionDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + RTTIBuilder b(Type::typeinfofunction); + // TypeInfo base + b.push_typeinfo(decl->tinfo->nextOf()); + // string deco + b.push_string(decl->tinfo->deco); + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoDelegateDeclaration *decl) { + IF_LOG Logger::println("TypeInfoDelegateDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + assert(decl->tinfo->ty == Tdelegate); + Type *ret_type = decl->tinfo->nextOf()->nextOf(); + + RTTIBuilder b(Type::typeinfodelegate); + // TypeInfo base + b.push_typeinfo(ret_type); + // string deco + b.push_string(decl->tinfo->deco); + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoStructDeclaration *decl) { + IF_LOG Logger::println("TypeInfoStructDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + // make sure struct is resolved + assert(decl->tinfo->ty == Tstruct); + TypeStruct *tc = static_cast(decl->tinfo); + StructDeclaration *sd = tc->sym; + + // handle opaque structs + if (!sd->members) { + RTTIBuilder b(Type::typeinfostruct); + b.finalize(getIrGlobal(decl)); + return; } - emitTypeMetadata(decl); - - // this is a declaration of a builtin __initZ var - if (builtinTypeInfo(decl->tinfo)) { - LLGlobalVariable* g = isaGlobalVar(irg->value); - g->setLinkage(llvm::GlobalValue::ExternalLinkage); - return; + // can't emit typeinfo for forward declarations + if (sd->sizeok != SIZEOKdone) { + sd->error("cannot emit TypeInfo for forward declaration"); + fatal(); } - // define custom typedef - LLVMDefineVisitor v; - decl->accept(&v); -} + DtoResolveStruct(sd); -/* ========================================================================= */ + if (TemplateInstance *ti = sd->isInstantiated()) { + if (!ti->needsCodegen()) { + assert(ti->minst || sd->requestTypeInfo); -void TypeInfoClassDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p) -{ - // For classes, the TypeInfo is in fact a ClassInfo instance and emitted - // as a __ClassZ symbol. For interfaces, the __InterfaceZ symbol is - // referenced as "info" member in a (normal) TypeInfo_Interface instance. - IrGlobal *irg = getIrGlobal(decl, true); + // We won't emit ti, so emit the special member functions in here. + if (sd->xeq && sd->xeq != StructDeclaration::xerreq) { + Declaration_codegen(sd->xeq); + } + if (sd->xcmp && sd->xcmp != StructDeclaration::xerrcmp) { + Declaration_codegen(sd->xcmp); + } + if (FuncDeclaration *ftostr = search_toString(sd)) { + Declaration_codegen(ftostr); + } + if (sd->xhash) { + Declaration_codegen(sd->xhash); + } + if (sd->postblit) { + Declaration_codegen(sd->postblit); + } + if (sd->dtor) { + Declaration_codegen(sd->dtor); + } + } + } + IrAggr *iraggr = getIrAggr(sd); + RTTIBuilder b(Type::typeinfostruct); + + // On x86_64, class TypeInfo_Struct contains 2 additional fields + // (m_arg1/m_arg2) which are used for the X86_64 System V ABI varargs + // implementation. They are not present on any other cpu/os. + unsigned expectedFields = 12; + if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) + expectedFields += 2; + if (Type::typeinfostruct->fields.dim != expectedFields) { + error(Loc(), "Unexpected number of object.TypeInfo_Struct fields; " + "druntime version does not match compiler"); + fatal(); + } + + // char[] name + b.push_string(sd->toPrettyChars()); + + // void[] init + // The protocol is to write a null pointer for zero-initialized arrays. The + // length field is always needed for tsize(). + llvm::Constant *initPtr; + if (tc->isZeroInit(Loc())) + initPtr = getNullValue(getVoidPtrType()); + else + initPtr = iraggr->getInitSymbol(); + b.push_void_array(getTypeStoreSize(DtoType(tc)), initPtr); + + // toHash + FuncDeclaration *fd = sd->xhash; + b.push_funcptr(fd); + + // opEquals + fd = sd->xeq; + b.push_funcptr(fd); + + // opCmp + fd = sd->xcmp; + b.push_funcptr(fd); + + // toString + fd = search_toString(sd); + b.push_funcptr(fd); + + // uint m_flags; + unsigned hasptrs = tc->hasPointers() ? 1 : 0; + b.push_uint(hasptrs); + + // void function(void*) xdtor; + b.push_funcptr(sd->dtor); + + // void function(void*) xpostblit; + FuncDeclaration *xpostblit = sd->postblit; + if (xpostblit && sd->postblit->storage_class & STCdisable) + xpostblit = 0; + b.push_funcptr(xpostblit); + + // uint m_align; + b.push_uint(DtoAlignment(tc)); + + if (global.params.is64bit) { + // TypeInfo m_arg1; + // TypeInfo m_arg2; + Type *t = sd->arg1type; + for (unsigned i = 0; i < 2; i++) { + if (t) { + t = t->merge(); + b.push_typeinfo(t); + } else + b.push_null(Type::dtypeinfo->type); + + t = sd->arg2type; + } + } + + // immutable(void)* m_RTInfo; + // The cases where getRTInfo is null are not quite here, but the code is + // modelled after what DMD does. + if (sd->getRTInfo) + b.push(toConstElem(sd->getRTInfo, gIR)); + else if (!tc->hasPointers()) + b.push_size_as_vp(0); // no pointers + else + b.push_size_as_vp(1); // has pointers + + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoClassDeclaration *decl) { + llvm_unreachable( + "TypeInfoClassDeclaration::llvmDefine() should not be called, " + "as a custom Dsymbol::codegen() override is used"); + } + + /* ========================================================================= + */ + + void visit(TypeInfoInterfaceDeclaration *decl) { + IF_LOG Logger::println("TypeInfoInterfaceDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + // make sure interface is resolved assert(decl->tinfo->ty == Tclass); TypeClass *tc = static_cast(decl->tinfo); DtoResolveClass(tc->sym); - irg->value = getIrAggr(tc->sym)->getClassInfoSymbol(); - irg->type = irg->value->getType()->getContainedType(0); + RTTIBuilder b(Type::typeinfointerface); - if (!tc->sym->isInterfaceDeclaration()) - { - emitTypeMetadata(decl); + // TypeInfo base + b.push_classinfo(tc->sym); + + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoTupleDeclaration *decl) { + IF_LOG Logger::println("TypeInfoTupleDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + // create elements array + assert(decl->tinfo->ty == Ttuple); + TypeTuple *tu = static_cast(decl->tinfo); + + size_t dim = tu->arguments->dim; + std::vector arrInits; + arrInits.reserve(dim); + + LLType *tiTy = DtoType(Type::dtypeinfo->type); + + for (size_t i = 0; i < dim; i++) { + Parameter *arg = static_cast(tu->arguments->data[i]); + arrInits.push_back(DtoTypeInfoOf(arg->type, true)); } + + // build array + LLArrayType *arrTy = LLArrayType::get(tiTy, dim); + LLConstant *arrC = LLConstantArray::get(arrTy, arrInits); + + RTTIBuilder b(Type::typeinfotypelist); + + // push TypeInfo[] + b.push_array(arrC, dim, Type::dtypeinfo->type, NULL); + + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoConstDeclaration *decl) { + IF_LOG Logger::println("TypeInfoConstDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + RTTIBuilder b(Type::typeinfoconst); + // TypeInfo base + b.push_typeinfo(decl->tinfo->mutableOf()->merge()); + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoInvariantDeclaration *decl) { + IF_LOG Logger::println("TypeInfoInvariantDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + RTTIBuilder b(Type::typeinfoinvariant); + // TypeInfo base + b.push_typeinfo(decl->tinfo->mutableOf()->merge()); + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoSharedDeclaration *decl) { + IF_LOG Logger::println("TypeInfoSharedDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + RTTIBuilder b(Type::typeinfoshared); + // TypeInfo base + b.push_typeinfo(decl->tinfo->unSharedOf()->merge()); + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoWildDeclaration *decl) { + IF_LOG Logger::println("TypeInfoWildDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + RTTIBuilder b(Type::typeinfowild); + // TypeInfo base + b.push_typeinfo(decl->tinfo->mutableOf()->merge()); + // finish + b.finalize(getIrGlobal(decl)); + } + + /* ========================================================================= + */ + + void visit(TypeInfoVectorDeclaration *decl) { + IF_LOG Logger::println("TypeInfoVectorDeclaration::llvmDefine() %s", + decl->toChars()); + LOG_SCOPE; + + assert(decl->tinfo->ty == Tvector); + TypeVector *tv = static_cast(decl->tinfo); + + RTTIBuilder b(Type::typeinfovector); + // TypeInfo base + b.push_typeinfo(tv->basetype); + // finish + b.finalize(getIrGlobal(decl)); + } +}; + +/* ========================================================================= */ + +void TypeInfoDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p) { + IF_LOG Logger::println("TypeInfoDeclaration::codegen(%s)", + decl->toPrettyChars()); + LOG_SCOPE; + + if (decl->ir.isDefined()) + return; + decl->ir.setDefined(); + + std::string mangled(mangle(decl)); + IF_LOG { + Logger::println("type = '%s'", decl->tinfo->toChars()); + Logger::println("typeinfo mangle: %s", mangled.c_str()); + } + + IrGlobal *irg = getIrGlobal(decl, true); + irg->value = gIR->module.getGlobalVariable(mangled); + if (irg->value) { + irg->type = irg->value->getType()->getContainedType(0); + assert(irg->type->isStructTy()); + } else { + if (builtinTypeInfo( + decl->tinfo)) // this is a declaration of a builtin __initZ var + irg->type = Type::dtypeinfo->type->ctype->isClass()->getMemoryLLType(); + else + irg->type = LLStructType::create(gIR->context(), decl->toPrettyChars()); + irg->value = new llvm::GlobalVariable(gIR->module, irg->type, true, + llvm::GlobalValue::ExternalLinkage, + NULL, mangled); + } + + emitTypeMetadata(decl); + + // this is a declaration of a builtin __initZ var + if (builtinTypeInfo(decl->tinfo)) { + LLGlobalVariable *g = isaGlobalVar(irg->value); + g->setLinkage(llvm::GlobalValue::ExternalLinkage); + return; + } + + // define custom typedef + LLVMDefineVisitor v; + decl->accept(&v); +} + +/* ========================================================================= */ + +void TypeInfoClassDeclaration_codegen(TypeInfoDeclaration *decl, IRState *p) { + // For classes, the TypeInfo is in fact a ClassInfo instance and emitted + // as a __ClassZ symbol. For interfaces, the __InterfaceZ symbol is + // referenced as "info" member in a (normal) TypeInfo_Interface instance. + IrGlobal *irg = getIrGlobal(decl, true); + + assert(decl->tinfo->ty == Tclass); + TypeClass *tc = static_cast(decl->tinfo); + DtoResolveClass(tc->sym); + + irg->value = getIrAggr(tc->sym)->getClassInfoSymbol(); + irg->type = irg->value->getType()->getContainedType(0); + + if (!tc->sym->isInterfaceDeclaration()) { + emitTypeMetadata(decl); + } } diff --git a/gen/warnings.cpp b/gen/warnings.cpp index 9326811597..abc64ac8cb 100644 --- a/gen/warnings.cpp +++ b/gen/warnings.cpp @@ -10,23 +10,21 @@ #include "gen/warnings.h" #include "mtype.h" -void warnInvalidPrintfCall(Loc loc, Expression* arguments, size_t nargs) -{ - Expression* arg = arguments; +void warnInvalidPrintfCall(Loc loc, Expression *arguments, size_t nargs) { + Expression *arg = arguments; - // make sure first argument is a string literal, or we can't do much - // TODO make it smarter ? - if (arg->op != TOKstring) - return; // assume valid + // make sure first argument is a string literal, or we can't do much + // TODO make it smarter ? + if (arg->op != TOKstring) + return; // assume valid - StringExp* strexp = static_cast(arg); + StringExp *strexp = static_cast(arg); - // not wchar or dhar - if (strexp->sz != 1) - { - warning(loc, "printf does not support wchar and dchar strings"); - return; - } + // not wchar or dhar + if (strexp->sz != 1) { + warning(loc, "printf does not support wchar and dchar strings"); + return; + } #if 0 // check the format string diff --git a/gen/warnings.h b/gen/warnings.h index f08c21b4e1..c90ce552c8 100644 --- a/gen/warnings.h +++ b/gen/warnings.h @@ -17,6 +17,6 @@ #include "expression.h" #include "mars.h" -void warnInvalidPrintfCall(Loc loc, Expression* arguments, size_t nargs); +void warnInvalidPrintfCall(Loc loc, Expression *arguments, size_t nargs); #endif // LDC_GEN_WARNINGS_H diff --git a/ir/iraggr.cpp b/ir/iraggr.cpp index 2f1bc68656..b18c592ade 100644 --- a/ir/iraggr.cpp +++ b/ir/iraggr.cpp @@ -25,48 +25,46 @@ ////////////////////////////////////////////////////////////////////////////// -IrAggr::IrAggr(AggregateDeclaration* aggr) -: aggrdecl(aggr), - type(aggr->type), - // above still need to be looked at - init_type(LLStructType::create(gIR->context(), std::string(aggr->toPrettyChars()) + "_init")) -{} +IrAggr::IrAggr(AggregateDeclaration *aggr) + : aggrdecl(aggr), type(aggr->type), + // above still need to be looked at + init_type(LLStructType::create( + gIR->context(), std::string(aggr->toPrettyChars()) + "_init")) {} ////////////////////////////////////////////////////////////////////////////// -LLGlobalVariable * IrAggr::getInitSymbol() -{ - if (init) - return init; - - // create the initZ symbol - std::string initname("_D"); - initname.append(mangle(aggrdecl)); - initname.append("6__initZ"); - - init = getOrCreateGlobal(aggrdecl->loc, - gIR->module, init_type, true, llvm::GlobalValue::ExternalLinkage, NULL, initname); - - // set alignment - init->setAlignment(DtoAlignment(type)); - +LLGlobalVariable *IrAggr::getInitSymbol() { + if (init) return init; + + // create the initZ symbol + std::string initname("_D"); + initname.append(mangle(aggrdecl)); + initname.append("6__initZ"); + + init = getOrCreateGlobal(aggrdecl->loc, gIR->module, init_type, true, + llvm::GlobalValue::ExternalLinkage, NULL, initname); + + // set alignment + init->setAlignment(DtoAlignment(type)); + + return init; } ////////////////////////////////////////////////////////////////////////////// -llvm::Constant * IrAggr::getDefaultInit() -{ - if (constInit) - return constInit; - - IF_LOG Logger::println("Building default initializer for %s", aggrdecl->toPrettyChars()); - LOG_SCOPE; - - DtoType(type); - VarInitMap noExplicitInitializers; - constInit = createInitializerConstant(noExplicitInitializers, init_type); +llvm::Constant *IrAggr::getDefaultInit() { + if (constInit) return constInit; + + IF_LOG Logger::println("Building default initializer for %s", + aggrdecl->toPrettyChars()); + LOG_SCOPE; + + DtoType(type); + VarInitMap noExplicitInitializers; + constInit = createInitializerConstant(noExplicitInitializers, init_type); + return constInit; } ////////////////////////////////////////////////////////////////////////////// @@ -75,300 +73,273 @@ llvm::Constant * IrAggr::getDefaultInit() // helper function that adds zero bytes to a vector of constants // FIXME A similar function is in ir/irtypeaggr.cpp -static inline -size_t add_zeros(llvm::SmallVectorImpl& constants, - size_t startOffset, size_t endOffset) -{ - assert(startOffset <= endOffset); - const size_t paddingSize = endOffset - startOffset; - if (paddingSize) - { - llvm::ArrayType* pad = llvm::ArrayType::get(llvm::Type::getInt8Ty(gIR->context()), paddingSize); - constants.push_back(llvm::Constant::getNullValue(pad)); - } - return paddingSize ? 1 : 0; +static inline size_t +add_zeros(llvm::SmallVectorImpl &constants, + size_t startOffset, size_t endOffset) { + assert(startOffset <= endOffset); + const size_t paddingSize = endOffset - startOffset; + if (paddingSize) { + llvm::ArrayType *pad = llvm::ArrayType::get( + llvm::Type::getInt8Ty(gIR->context()), paddingSize); + constants.push_back(llvm::Constant::getNullValue(pad)); + } + return paddingSize ? 1 : 0; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -typedef std::pair VarInitConst; +typedef std::pair VarInitConst; -static bool struct_init_data_sort(const VarInitConst& a, const VarInitConst& b) -{ - return (a.first && b.first) - ? a.first->offset < b.first->offset - : false; +static bool struct_init_data_sort(const VarInitConst &a, + const VarInitConst &b) { + return (a.first && b.first) ? a.first->offset < b.first->offset : false; } // helper function that returns the static default initializer of a variable -LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init) -{ - if (init) - { - return DtoConstInitializer(init->loc, vd->type, init); - } +LLConstant *get_default_initializer(VarDeclaration *vd, Initializer *init) { + if (init) { + return DtoConstInitializer(init->loc, vd->type, init); + } - if (vd->init) - { - return DtoConstInitializer(vd->init->loc, vd->type, vd->init); - } + if (vd->init) { + return DtoConstInitializer(vd->init->loc, vd->type, vd->init); + } - if (vd->type->size(vd->loc) == 0) - { - // We need to be able to handle void[0] struct members even if void has - // no default initializer. - return llvm::ConstantPointerNull::get(DtoPtrToType(vd->type)); - } - return DtoConstExpInit(vd->loc, vd->type, vd->type->defaultInit(vd->loc)); + if (vd->type->size(vd->loc) == 0) { + // We need to be able to handle void[0] struct members even if void has + // no default initializer. + return llvm::ConstantPointerNull::get(DtoPtrToType(vd->type)); + } + return DtoConstExpInit(vd->loc, vd->type, vd->type->defaultInit(vd->loc)); } -// return a constant array of type arrTypeD initialized with a constant value, or that constant value -static llvm::Constant* FillSArrayDims(Type* arrTypeD, llvm::Constant* init) -{ - // Check whether we actually need to expand anything. - // KLUDGE: We don't have the initializer type here, so we can only check - // the size without doing an expensive recursive D <-> LLVM type comparison. - // The better way to solve this would be to just fix the initializer - // codegen in any place where a scalar initializer might still be generated. - if (gDataLayout->getTypeStoreSize(init->getType()) >= arrTypeD->size()) - return init; - - if (arrTypeD->ty == Tsarray) - { - init = FillSArrayDims(arrTypeD->nextOf(), init); - size_t dim = static_cast(arrTypeD)->dim->toUInteger(); - llvm::ArrayType* arrty = llvm::ArrayType::get(init->getType(), dim); - return llvm::ConstantArray::get(arrty, - std::vector(dim, init)); - } +// return a constant array of type arrTypeD initialized with a constant value, +// or that constant value +static llvm::Constant *FillSArrayDims(Type *arrTypeD, llvm::Constant *init) { + // Check whether we actually need to expand anything. + // KLUDGE: We don't have the initializer type here, so we can only check + // the size without doing an expensive recursive D <-> LLVM type comparison. + // The better way to solve this would be to just fix the initializer + // codegen in any place where a scalar initializer might still be generated. + if (gDataLayout->getTypeStoreSize(init->getType()) >= arrTypeD->size()) return init; + + if (arrTypeD->ty == Tsarray) { + init = FillSArrayDims(arrTypeD->nextOf(), init); + size_t dim = static_cast(arrTypeD)->dim->toUInteger(); + llvm::ArrayType *arrty = llvm::ArrayType::get(init->getType(), dim); + return llvm::ConstantArray::get(arrty, + std::vector(dim, init)); + } + return init; } -llvm::Constant* IrAggr::createInitializerConstant( - const VarInitMap& explicitInitializers, - llvm::StructType* initializerType) -{ - IF_LOG Logger::println("Creating initializer constant for %s", aggrdecl->toChars()); - LOG_SCOPE; +llvm::Constant * +IrAggr::createInitializerConstant(const VarInitMap &explicitInitializers, + llvm::StructType *initializerType) { + IF_LOG Logger::println("Creating initializer constant for %s", + aggrdecl->toChars()); + LOG_SCOPE; - llvm::SmallVector constants; + llvm::SmallVector constants; - unsigned offset = 0; - if (type->ty == Tclass) - { - // add vtbl - constants.push_back(getVtblSymbol()); - offset += Target::ptrsize; + unsigned offset = 0; + if (type->ty == Tclass) { + // add vtbl + constants.push_back(getVtblSymbol()); + offset += Target::ptrsize; - // add monitor (except for C++ classes) - if (!aggrdecl->isClassDeclaration()->isCPPclass()) - { - constants.push_back(getNullValue(getVoidPtrType())); - offset += Target::ptrsize; - } + // add monitor (except for C++ classes) + if (!aggrdecl->isClassDeclaration()->isCPPclass()) { + constants.push_back(getNullValue(getVoidPtrType())); + offset += Target::ptrsize; } + } - // Add the initializers for the member fields. While we are traversing the - // class hierarchy, use the opportunity to populate interfacesWithVtbls if - // we haven't done so previously (due to e.g. ClassReferenceExp, we can - // have multiple initializer constants for a single class). - addFieldInitializers(constants, explicitInitializers, aggrdecl, offset, - interfacesWithVtbls.empty()); + // Add the initializers for the member fields. While we are traversing the + // class hierarchy, use the opportunity to populate interfacesWithVtbls if + // we haven't done so previously (due to e.g. ClassReferenceExp, we can + // have multiple initializer constants for a single class). + addFieldInitializers(constants, explicitInitializers, aggrdecl, offset, + interfacesWithVtbls.empty()); - // tail padding? - const size_t structsize = aggrdecl->size(Loc()); - if (offset < structsize) - { - add_zeros(constants, offset, structsize); - } + // tail padding? + const size_t structsize = aggrdecl->size(Loc()); + if (offset < structsize) { + add_zeros(constants, offset, structsize); + } - // get initializer type - if (!initializerType || initializerType->isOpaque()) - { - llvm::SmallVector types; - types.reserve(constants.size()); - for (auto c : constants) - types.push_back(c->getType()); - if (!initializerType) - initializerType = LLStructType::get(gIR->context(), types, isPacked()); - else - initializerType->setBody(types, isPacked()); - } + // get initializer type + if (!initializerType || initializerType->isOpaque()) { + llvm::SmallVector types; + types.reserve(constants.size()); + for (auto c : constants) + types.push_back(c->getType()); + if (!initializerType) + initializerType = LLStructType::get(gIR->context(), types, isPacked()); + else + initializerType->setBody(types, isPacked()); + } - // build constant - assert(!constants.empty()); - llvm::Constant* c = LLConstantStruct::get(initializerType, constants); - IF_LOG Logger::cout() << "final initializer: " << *c << std::endl; - return c; + // build constant + assert(!constants.empty()); + llvm::Constant *c = LLConstantStruct::get(initializerType, constants); + IF_LOG Logger::cout() << "final initializer: " << *c << std::endl; + return c; } void IrAggr::addFieldInitializers( - llvm::SmallVectorImpl& constants, - const VarInitMap& explicitInitializers, - AggregateDeclaration* decl, - unsigned& offset, - bool populateInterfacesWithVtbls - ) -{ - if (ClassDeclaration* cd = decl->isClassDeclaration()) - { - if (cd->baseClass) + llvm::SmallVectorImpl &constants, + const VarInitMap &explicitInitializers, AggregateDeclaration *decl, + unsigned &offset, bool populateInterfacesWithVtbls) { + if (ClassDeclaration *cd = decl->isClassDeclaration()) { + if (cd->baseClass) { + addFieldInitializers(constants, explicitInitializers, cd->baseClass, + offset, populateInterfacesWithVtbls); + } + } + + // Build up vector with one-to-one mapping to field indices. + const size_t n = decl->fields.dim; + llvm::SmallVector data(n); + + // Fill in explicit initializers. + for (size_t i = 0; i < n; ++i) { + VarDeclaration *vd = decl->fields[i]; + VarInitMap::const_iterator expl = explicitInitializers.find(vd); + if (expl != explicitInitializers.end()) + data[i] = *expl; + } + + // Fill in implicit initializers + for (size_t i = 0; i < n; i++) { + if (data[i].first) + continue; + + VarDeclaration *vd = decl->fields[i]; + + /* Skip void initializers for unions. DMD bug 3991: + union X { - addFieldInitializers(constants, explicitInitializers, - cd->baseClass, offset, populateInterfacesWithVtbls); + int a = void; + dchar b = 'a'; } + */ + if (decl->isUnionDeclaration() && vd->init && vd->init->isVoidInitializer()) + continue; + + unsigned vd_begin = vd->offset; + unsigned vd_end = vd_begin + vd->type->size(); + + /* Skip zero size fields like zero-length static arrays, LDC issue 812: + class B { + ubyte[0] test; + } + */ + if (vd_begin == vd_end) + continue; + + // make sure it doesn't overlap any explicit initializers. + bool overlaps = false; + if (type->ty == Tstruct) { + // Only structs and unions can have overlapping fields. + for (size_t j = 0; j < n; ++j) { + if (i == j || !data[j].first) + continue; + + VarDeclaration *it = decl->fields[j]; + unsigned f_begin = it->offset; + unsigned f_end = f_begin + it->type->size(); + + if (vd_begin >= f_end || vd_end <= f_begin) + continue; + + overlaps = true; + break; + } + } + // add if no overlap found + if (!overlaps) { + IF_LOG Logger::println("Implicit initializer: %s @+%u", vd->toChars(), + vd->offset); + LOG_SCOPE; + + data[i].first = vd; + data[i].second = get_default_initializer(vd, NULL); + } + } + + // Sort data array by offset. + // TODO: Figure out whether this is really necessary, fields should already + // be in offset order. Not having do do this would mean we could use a plain + // llvm::Constant* vector for initializers and avoid all the VarInitConst + // business. + std::sort(data.begin(), data.end(), struct_init_data_sort); + + // build array of constants and make sure explicit zero padding is inserted + // when necessary. + for (size_t i = 0; i < n; i++) { + VarDeclaration *vd = data[i].first; + if (vd == NULL) + continue; + + // Explicitly zero the padding as per TDPL §7.1.1. Otherwise, it would + // be left uninitialized by LLVM. + if (offset < vd->offset) { + add_zeros(constants, offset, vd->offset); + offset = vd->offset; } - // Build up vector with one-to-one mapping to field indices. - const size_t n = decl->fields.dim; - llvm::SmallVector data(n); + IF_LOG Logger::println("adding field %s", vd->toChars()); - // Fill in explicit initializers. - for (size_t i = 0; i < n; ++i) - { - VarDeclaration* vd = decl->fields[i]; - VarInitMap::const_iterator expl = explicitInitializers.find(vd); - if (expl != explicitInitializers.end()) - data[i] = *expl; - } - - // Fill in implicit initializers - for (size_t i = 0; i < n; i++) - { - if (data[i].first) continue; - - VarDeclaration* vd = decl->fields[i]; - - /* Skip void initializers for unions. DMD bug 3991: - union X - { - int a = void; - dchar b = 'a'; - } - */ - if (decl->isUnionDeclaration() && vd->init && vd->init->isVoidInitializer()) - continue; - - unsigned vd_begin = vd->offset; - unsigned vd_end = vd_begin + vd->type->size(); - - /* Skip zero size fields like zero-length static arrays, LDC issue 812: - class B { - ubyte[0] test; - } - */ - if (vd_begin == vd_end) - continue; - - // make sure it doesn't overlap any explicit initializers. - bool overlaps = false; - if (type->ty == Tstruct) - { - // Only structs and unions can have overlapping fields. - for (size_t j = 0; j < n; ++j) - { - if (i == j || !data[j].first) - continue; - - VarDeclaration* it = decl->fields[j]; - unsigned f_begin = it->offset; - unsigned f_end = f_begin + it->type->size(); - - if (vd_begin >= f_end || vd_end <= f_begin) - continue; - - overlaps = true; - break; - } - } - // add if no overlap found - if (!overlaps) - { - IF_LOG Logger::println("Implicit initializer: %s @+%u", vd->toChars(), vd->offset); - LOG_SCOPE; - - data[i].first = vd; - data[i].second = get_default_initializer(vd, NULL); - } - } - - // Sort data array by offset. - // TODO: Figure out whether this is really necessary, fields should already - // be in offset order. Not having do do this would mean we could use a plain - // llvm::Constant* vector for initializers and avoid all the VarInitConst business. - std::sort(data.begin(), data.end(), struct_init_data_sort); - - // build array of constants and make sure explicit zero padding is inserted when necessary. - for (size_t i = 0; i < n; i++) - { - VarDeclaration* vd = data[i].first; - if (vd == NULL) - continue; - - // Explicitly zero the padding as per TDPL §7.1.1. Otherwise, it would - // be left uninitialized by LLVM. - if (offset < vd->offset) - { - add_zeros(constants, offset, vd->offset); - offset = vd->offset; - } - - IF_LOG Logger::println("adding field %s", vd->toChars()); - - constants.push_back(FillSArrayDims(vd->type, data[i].second)); - offset += getMemberSize(vd->type); - } - - if (ClassDeclaration* cd = decl->isClassDeclaration()) - { - // has interface vtbls? - if (cd->vtblInterfaces && cd->vtblInterfaces->dim > 0) - { - // Align interface infos to pointer size. - unsigned aligned = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); - if (offset < aligned) - { - add_zeros(constants, offset, aligned); - offset = aligned; - } - - // false when it's not okay to use functions from super classes - bool newinsts = (cd == aggrdecl->isClassDeclaration()); - - size_t inter_idx = interfacesWithVtbls.size(); - - offset = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); - - for (auto bc : *cd->vtblInterfaces) - { - constants.push_back(getInterfaceVtbl(bc, newinsts, inter_idx)); - offset += Target::ptrsize; - inter_idx++; - - if (populateInterfacesWithVtbls) - interfacesWithVtbls.push_back(bc); - } - } + constants.push_back(FillSArrayDims(vd->type, data[i].second)); + offset += getMemberSize(vd->type); + } + + if (ClassDeclaration *cd = decl->isClassDeclaration()) { + // has interface vtbls? + if (cd->vtblInterfaces && cd->vtblInterfaces->dim > 0) { + // Align interface infos to pointer size. + unsigned aligned = + (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); + if (offset < aligned) { + add_zeros(constants, offset, aligned); + offset = aligned; + } + + // false when it's not okay to use functions from super classes + bool newinsts = (cd == aggrdecl->isClassDeclaration()); + + size_t inter_idx = interfacesWithVtbls.size(); + + offset = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); + + for (auto bc : *cd->vtblInterfaces) { + constants.push_back(getInterfaceVtbl(bc, newinsts, inter_idx)); + offset += Target::ptrsize; + inter_idx++; + + if (populateInterfacesWithVtbls) + interfacesWithVtbls.push_back(bc); + } } + } } -IrAggr *getIrAggr(AggregateDeclaration *decl, bool create) -{ - if (!isIrAggrCreated(decl) && create) - { - assert(decl->ir.irAggr == NULL); - decl->ir.irAggr = new IrAggr(decl); - decl->ir.m_type = IrDsymbol::AggrType; - } - assert(decl->ir.irAggr != NULL); - return decl->ir.irAggr; +IrAggr *getIrAggr(AggregateDeclaration *decl, bool create) { + if (!isIrAggrCreated(decl) && create) { + assert(decl->ir.irAggr == NULL); + decl->ir.irAggr = new IrAggr(decl); + decl->ir.m_type = IrDsymbol::AggrType; + } + assert(decl->ir.irAggr != NULL); + return decl->ir.irAggr; } -bool isIrAggrCreated(AggregateDeclaration *decl) -{ - int t = decl->ir.type(); - assert(t == IrDsymbol::AggrType || t == IrDsymbol::NotSet); - return t == IrDsymbol::AggrType; +bool isIrAggrCreated(AggregateDeclaration *decl) { + int t = decl->ir.type(); + assert(t == IrDsymbol::AggrType || t == IrDsymbol::NotSet); + return t == IrDsymbol::AggrType; } diff --git a/ir/iraggr.h b/ir/iraggr.h index 1095b67d07..b0c9eabef3 100644 --- a/ir/iraggr.h +++ b/ir/iraggr.h @@ -23,135 +23,132 @@ class StructInitializer; namespace llvm { - class Constant; - class StructType; +class Constant; +class StructType; } ////////////////////////////////////////////////////////////////////////////// // represents a struct or class // it is used during codegen to hold all the vital info we need -struct IrAggr -{ - /// Constructor. - explicit IrAggr(AggregateDeclaration* agg); +struct IrAggr { + /// Constructor. + explicit IrAggr(AggregateDeclaration *agg); - ////////////////////////////////////////////////////////////////////////// - // public fields, - // FIXME this is basically stuff I just haven't gotten around to yet. + ////////////////////////////////////////////////////////////////////////// + // public fields, + // FIXME this is basically stuff I just haven't gotten around to yet. - /// The D aggregate. - AggregateDeclaration* aggrdecl = nullptr; + /// The D aggregate. + AggregateDeclaration *aggrdecl = nullptr; - /// Aggregate D type. - Type* type = nullptr; + /// Aggregate D type. + Type *type = nullptr; - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - /// Create the __initZ symbol lazily. - llvm::GlobalVariable* getInitSymbol(); - /// Builds the __initZ initializer constant lazily. - llvm::Constant* getDefaultInit(); + /// Create the __initZ symbol lazily. + llvm::GlobalVariable *getInitSymbol(); + /// Builds the __initZ initializer constant lazily. + llvm::Constant *getDefaultInit(); - /// Create the __vtblZ symbol lazily. - llvm::GlobalVariable* getVtblSymbol(); - /// Builds the __vtblZ initializer constant lazily. - llvm::Constant* getVtblInit(); + /// Create the __vtblZ symbol lazily. + llvm::GlobalVariable *getVtblSymbol(); + /// Builds the __vtblZ initializer constant lazily. + llvm::Constant *getVtblInit(); - /// Create the __ClassZ/__InterfaceZ symbol lazily. - llvm::GlobalVariable* getClassInfoSymbol(); - /// Builds the __ClassZ/__InterfaceZ initializer constant lazily. - llvm::Constant* getClassInfoInit(); + /// Create the __ClassZ/__InterfaceZ symbol lazily. + llvm::GlobalVariable *getClassInfoSymbol(); + /// Builds the __ClassZ/__InterfaceZ initializer constant lazily. + llvm::Constant *getClassInfoInit(); - /// Create the __interfaceInfos symbol lazily. - llvm::GlobalVariable* getInterfaceArraySymbol(); + /// Create the __interfaceInfos symbol lazily. + llvm::GlobalVariable *getInterfaceArraySymbol(); - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - /// Initialize interface. - void initializeInterface(); + /// Initialize interface. + void initializeInterface(); - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - using VarInitMap = std::map; + using VarInitMap = std::map; - /// Creates an initializer constant for the struct type with the given - /// fields set to the provided constants. The remaining space (not - /// explicitly specified fields, padding) is default-initialized. - /// - /// The optional initializerType parmeter can be used to specify the exact - /// LLVM type to use for the initializer. If non-null and non-opaque, the - /// type must exactly match the generated constant. This parameter is used - /// mainly for supporting legacy code. - /// - /// Note that in the general case (if e.g. unions are involved), the - /// returned type is not necessarily the same as getLLType(). - llvm::Constant* createInitializerConstant( - const VarInitMap& explicitInitializers, - llvm::StructType* initializerType = 0); + /// Creates an initializer constant for the struct type with the given + /// fields set to the provided constants. The remaining space (not + /// explicitly specified fields, padding) is default-initialized. + /// + /// The optional initializerType parmeter can be used to specify the exact + /// LLVM type to use for the initializer. If non-null and non-opaque, the + /// type must exactly match the generated constant. This parameter is used + /// mainly for supporting legacy code. + /// + /// Note that in the general case (if e.g. unions are involved), the + /// returned type is not necessarily the same as getLLType(). + llvm::Constant * + createInitializerConstant(const VarInitMap &explicitInitializers, + llvm::StructType *initializerType = 0); protected: - /// Static default initializer global. - llvm::GlobalVariable* init = nullptr; - /// Static default initializer constant. - llvm::Constant* constInit = nullptr; - /// Static default initialier type. - llvm::StructType* init_type = nullptr; + /// Static default initializer global. + llvm::GlobalVariable *init = nullptr; + /// Static default initializer constant. + llvm::Constant *constInit = nullptr; + /// Static default initialier type. + llvm::StructType *init_type = nullptr; - /// Vtbl global. - llvm::GlobalVariable* vtbl = nullptr; - /// Vtbl initializer constant. - llvm::Constant* constVtbl = nullptr; + /// Vtbl global. + llvm::GlobalVariable *vtbl = nullptr; + /// Vtbl initializer constant. + llvm::Constant *constVtbl = nullptr; - /// ClassInfo global. - llvm::GlobalVariable* classInfo = nullptr; - /// ClassInfo initializer constant. - llvm::Constant* constClassInfo = nullptr; + /// ClassInfo global. + llvm::GlobalVariable *classInfo = nullptr; + /// ClassInfo initializer constant. + llvm::Constant *constClassInfo = nullptr; - /// Map for mapping ClassDeclaration* to LLVM GlobalVariable. - using ClassGlobalMap = std::map; + /// Map for mapping ClassDeclaration* to LLVM GlobalVariable. + using ClassGlobalMap = std::map; - /// Map from of interface vtbls implemented by this class. - ClassGlobalMap interfaceVtblMap; + /// Map from of interface vtbls implemented by this class. + ClassGlobalMap interfaceVtblMap; - /// Interface info array global. - /// Basically: static object.Interface[num_interfaces] - llvm::GlobalVariable* classInterfacesArray = nullptr; + /// Interface info array global. + /// Basically: static object.Interface[num_interfaces] + llvm::GlobalVariable *classInterfacesArray = nullptr; - /// std::vector of BaseClass* - using BaseClassVector = std::vector; + /// std::vector of BaseClass* + using BaseClassVector = std::vector; - /// Array of all interface vtbl implementations - in order - implemented - /// by this class. - /// Corresponds to the Interface instances needed to be output. - BaseClassVector interfacesWithVtbls; + /// Array of all interface vtbl implementations - in order - implemented + /// by this class. + /// Corresponds to the Interface instances needed to be output. + BaseClassVector interfacesWithVtbls; - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - /// Returns vtbl for interface implementation, creates it if not already built. - llvm::GlobalVariable* getInterfaceVtbl( - BaseClass* b, - bool new_inst, - size_t interfaces_index); + /// Returns vtbl for interface implementation, creates it if not already + /// built. + llvm::GlobalVariable *getInterfaceVtbl(BaseClass *b, bool new_inst, + size_t interfaces_index); - // FIXME make this a member instead - friend llvm::Constant* DtoDefineClassInfo(ClassDeclaration* cd); + // FIXME make this a member instead + friend llvm::Constant *DtoDefineClassInfo(ClassDeclaration *cd); - /// Create the Interface[] interfaces ClassInfo field initializer. - llvm::Constant* getClassInfoInterfaces(); + /// Create the Interface[] interfaces ClassInfo field initializer. + llvm::Constant *getClassInfoInterfaces(); - /// Returns true, if the LLVM struct type for the aggregate is declared as packed - bool isPacked() const; + /// Returns true, if the LLVM struct type for the aggregate is declared as + /// packed + bool isPacked() const; private: - /// Recursively adds all the initializers for the given aggregate and, in - /// case of a class type, all its base classes. - void addFieldInitializers( - llvm::SmallVectorImpl& constants, - const VarInitMap& explicitInitializers, - AggregateDeclaration* decl, - unsigned& offset, - bool populateInterfacesWithVtbls); + /// Recursively adds all the initializers for the given aggregate and, in + /// case of a class type, all its base classes. + void addFieldInitializers(llvm::SmallVectorImpl &constants, + const VarInitMap &explicitInitializers, + AggregateDeclaration *decl, unsigned &offset, + bool populateInterfacesWithVtbls); }; ////////////////////////////////////////////////////////////////////////////// diff --git a/ir/irclass.cpp b/ir/irclass.cpp index 85eca7a858..c96e2b33ea 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -36,523 +36,502 @@ ////////////////////////////////////////////////////////////////////////////// -extern LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init); +extern LLConstant *get_default_initializer(VarDeclaration *vd, + Initializer *init); -extern LLConstant* DtoDefineClassInfo(ClassDeclaration* cd); +extern LLConstant *DtoDefineClassInfo(ClassDeclaration *cd); ////////////////////////////////////////////////////////////////////////////// -LLGlobalVariable * IrAggr::getVtblSymbol() -{ - if (vtbl) - return vtbl; - - // create the initZ symbol - std::string initname("_D"); - initname.append(mangle(aggrdecl)); - initname.append("6__vtblZ"); - - LLType* vtblTy = stripModifiers(type)->ctype->isClass()->getVtbl(); - - vtbl = getOrCreateGlobal(aggrdecl->loc, - gIR->module, vtblTy, true, llvm::GlobalValue::ExternalLinkage, NULL, initname); - +LLGlobalVariable *IrAggr::getVtblSymbol() { + if (vtbl) return vtbl; + + // create the initZ symbol + std::string initname("_D"); + initname.append(mangle(aggrdecl)); + initname.append("6__vtblZ"); + + LLType *vtblTy = stripModifiers(type)->ctype->isClass()->getVtbl(); + + vtbl = getOrCreateGlobal(aggrdecl->loc, gIR->module, vtblTy, true, + llvm::GlobalValue::ExternalLinkage, NULL, initname); + + return vtbl; } ////////////////////////////////////////////////////////////////////////////// -LLGlobalVariable * IrAggr::getClassInfoSymbol() -{ - if (classInfo) - return classInfo; - - // create the initZ symbol - std::string initname("_D"); - initname.append(mangle(aggrdecl)); - - if (aggrdecl->isInterfaceDeclaration()) - initname.append("11__InterfaceZ"); - else - initname.append("7__ClassZ"); - - // The type is also ClassInfo for interfaces – the actual TypeInfo for them - // is a TypeInfo_Interface instance that references __ClassZ in its "base" - // member. - ClassDeclaration* cinfo = Type::typeinfoclass; - DtoType(cinfo->type); - IrTypeClass* tc = stripModifiers(cinfo->type)->ctype->isClass(); - assert(tc && "invalid ClassInfo type"); - - // classinfos cannot be constants since they're used as locks for synchronized - classInfo = getOrCreateGlobal(aggrdecl->loc, - gIR->module, tc->getMemoryLLType(), false, - llvm::GlobalValue::ExternalLinkage, NULL, initname); - - // Generate some metadata on this ClassInfo if it's for a class. - ClassDeclaration* classdecl = aggrdecl->isClassDeclaration(); - if (classdecl && !aggrdecl->isInterfaceDeclaration()) { - // Gather information - LLType* type = DtoType(aggrdecl->type); - LLType* bodyType = llvm::cast(type)->getElementType(); - bool hasDestructor = (classdecl->dtor != NULL); - bool hasCustomDelete = (classdecl->aggDelete != NULL); - // Construct the fields -#if LDC_LLVM_VER >= 306 - llvm::Metadata* mdVals[CD_NumFields]; - mdVals[CD_BodyType] = llvm::ConstantAsMetadata::get(llvm::UndefValue::get(bodyType)); - mdVals[CD_Finalize] = llvm::ConstantAsMetadata::get(LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasDestructor)); - mdVals[CD_CustomDelete] = llvm::ConstantAsMetadata::get(LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasCustomDelete)); -#else - MDNodeField* mdVals[CD_NumFields]; - mdVals[CD_BodyType] = llvm::UndefValue::get(bodyType); - mdVals[CD_Finalize] = LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasDestructor); - mdVals[CD_CustomDelete] = LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasCustomDelete); -#endif - // Construct the metadata and insert it into the module. - llvm::SmallString<64> name; - llvm::NamedMDNode* node = gIR->module.getOrInsertNamedMetadata( - llvm::Twine(CD_PREFIX, initname).toStringRef(name)); - node->addOperand(llvm::MDNode::get(gIR->context(), - llvm::makeArrayRef(mdVals, CD_NumFields))); - } - +LLGlobalVariable *IrAggr::getClassInfoSymbol() { + if (classInfo) return classInfo; + + // create the initZ symbol + std::string initname("_D"); + initname.append(mangle(aggrdecl)); + + if (aggrdecl->isInterfaceDeclaration()) + initname.append("11__InterfaceZ"); + else + initname.append("7__ClassZ"); + + // The type is also ClassInfo for interfaces – the actual TypeInfo for them + // is a TypeInfo_Interface instance that references __ClassZ in its "base" + // member. + ClassDeclaration *cinfo = Type::typeinfoclass; + DtoType(cinfo->type); + IrTypeClass *tc = stripModifiers(cinfo->type)->ctype->isClass(); + assert(tc && "invalid ClassInfo type"); + + // classinfos cannot be constants since they're used as locks for synchronized + classInfo = getOrCreateGlobal( + aggrdecl->loc, gIR->module, tc->getMemoryLLType(), false, + llvm::GlobalValue::ExternalLinkage, NULL, initname); + + // Generate some metadata on this ClassInfo if it's for a class. + ClassDeclaration *classdecl = aggrdecl->isClassDeclaration(); + if (classdecl && !aggrdecl->isInterfaceDeclaration()) { + // Gather information + LLType *type = DtoType(aggrdecl->type); + LLType *bodyType = llvm::cast(type)->getElementType(); + bool hasDestructor = (classdecl->dtor != NULL); + bool hasCustomDelete = (classdecl->aggDelete != NULL); +// Construct the fields +#if LDC_LLVM_VER >= 306 + llvm::Metadata *mdVals[CD_NumFields]; + mdVals[CD_BodyType] = + llvm::ConstantAsMetadata::get(llvm::UndefValue::get(bodyType)); + mdVals[CD_Finalize] = llvm::ConstantAsMetadata::get( + LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasDestructor)); + mdVals[CD_CustomDelete] = llvm::ConstantAsMetadata::get( + LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasCustomDelete)); +#else + MDNodeField *mdVals[CD_NumFields]; + mdVals[CD_BodyType] = llvm::UndefValue::get(bodyType); + mdVals[CD_Finalize] = + LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasDestructor); + mdVals[CD_CustomDelete] = + LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasCustomDelete); +#endif + // Construct the metadata and insert it into the module. + llvm::SmallString<64> name; + llvm::NamedMDNode *node = gIR->module.getOrInsertNamedMetadata( + llvm::Twine(CD_PREFIX, initname).toStringRef(name)); + node->addOperand(llvm::MDNode::get( + gIR->context(), llvm::makeArrayRef(mdVals, CD_NumFields))); + } + + return classInfo; } ////////////////////////////////////////////////////////////////////////////// -LLGlobalVariable * IrAggr::getInterfaceArraySymbol() -{ - if (classInterfacesArray) - return classInterfacesArray; - - ClassDeclaration* cd = aggrdecl->isClassDeclaration(); - - size_t n = stripModifiers(type)->ctype->isClass()->getNumInterfaceVtbls(); - assert(n > 0 && "getting ClassInfo.interfaces storage symbol, but we " - "don't implement any interfaces"); - - LLType* InterfaceTy = DtoType(Type::typeinfoclass->fields[3]->type->nextOf()); - - // create Interface[N] - LLArrayType* array_type = llvm::ArrayType::get(InterfaceTy,n); - - // put it in a global - std::string name("_D"); - name.append(mangle(cd)); - name.append("16__interfaceInfosZ"); - - // We keep this as external for now and only consider template linkage if - // we emit the initializer later. - classInterfacesArray = getOrCreateGlobal(cd->loc, gIR->module, - array_type, true, llvm::GlobalValue::ExternalLinkage, NULL, name); - +LLGlobalVariable *IrAggr::getInterfaceArraySymbol() { + if (classInterfacesArray) return classInterfacesArray; + + ClassDeclaration *cd = aggrdecl->isClassDeclaration(); + + size_t n = stripModifiers(type)->ctype->isClass()->getNumInterfaceVtbls(); + assert(n > 0 && "getting ClassInfo.interfaces storage symbol, but we " + "don't implement any interfaces"); + + LLType *InterfaceTy = DtoType(Type::typeinfoclass->fields[3]->type->nextOf()); + + // create Interface[N] + LLArrayType *array_type = llvm::ArrayType::get(InterfaceTy, n); + + // put it in a global + std::string name("_D"); + name.append(mangle(cd)); + name.append("16__interfaceInfosZ"); + + // We keep this as external for now and only consider template linkage if + // we emit the initializer later. + classInterfacesArray = + getOrCreateGlobal(cd->loc, gIR->module, array_type, true, + llvm::GlobalValue::ExternalLinkage, NULL, name); + + return classInterfacesArray; } ////////////////////////////////////////////////////////////////////////////// -LLConstant * IrAggr::getVtblInit() -{ - if (constVtbl) - return constVtbl; - - IF_LOG Logger::println("Building vtbl initializer"); - LOG_SCOPE; - - ClassDeclaration* cd = aggrdecl->isClassDeclaration(); - assert(cd && "not class"); - - std::vector constants; - constants.reserve(cd->vtbl.dim); - - // start with the classinfo - llvm::Constant* c; - if (!cd->isCPPclass()) - { - c = getClassInfoSymbol(); - c = DtoBitCast(c, DtoType(Type::typeinfoclass->type)); - constants.push_back(c); - } - - // add virtual function pointers - size_t n = cd->vtbl.dim; - for (size_t i = cd->vtblOffset(); i < n; i++) - { - Dsymbol* dsym = static_cast(cd->vtbl.data[i]); - assert(dsym && "null vtbl member"); - - FuncDeclaration* fd = dsym->isFuncDeclaration(); - assert(fd && "vtbl entry not a function"); - - if (cd->isAbstract() || (fd->isAbstract() && !fd->fbody)) - { - c = getNullValue(getPtrToType(DtoFunctionType(fd))); - } - else - { - DtoResolveFunction(fd); - assert(isIrFuncCreated(fd) && "invalid vtbl function"); - c = getIrFunc(fd)->func; - if (cd->isFuncHidden(fd)) - { /* fd is hidden from the view of this class. - * If fd overlaps with any function in the vtbl[], then - * issue 'hidden' error. - */ - for (size_t j = 1; j < n; j++) - { - if (j == i) - continue; - FuncDeclaration *fd2 = static_cast(cd->vtbl.data[j])->isFuncDeclaration(); - if (!fd2->ident->equals(fd->ident)) - continue; - if (fd->leastAsSpecialized(fd2) || fd2->leastAsSpecialized(fd)) - { - TypeFunction *tf = static_cast(fd->type); - if (tf->ty == Tfunction) - { - cd->error("use of %s%s is hidden by %s; use 'alias %s = %s.%s;' to introduce base class overload set", - fd->toPrettyChars(), - parametersTypeToChars(tf->parameters, tf->varargs), - cd->toChars(), - - fd->toChars(), - fd->parent->toChars(), - fd->toChars()); - } - else - { - cd->error("use of %s is hidden by %s", fd->toPrettyChars(), cd->toChars()); - } - fatal(); - break; - } - } - } - } - constants.push_back(c); - } - - // build the constant struct - LLType* vtblTy = stripModifiers(type)->ctype->isClass()->getVtbl(); -#ifndef NDEBUG - size_t nc = constants.size(); - - for (size_t i = 0; i < nc; ++i) - { - if (constants[i]->getType() != vtblTy->getContainedType(i)) - { - llvm::errs() << "type mismatch for entry # " << i << " in vtbl initializer\n"; - - constants[i]->getType()->dump(); - vtblTy->getContainedType(i)->dump(); - } - } - -#endif - constVtbl = LLConstantStruct::get(isaStruct(vtblTy), constants); - - assert(constVtbl->getType() == stripModifiers(type)->ctype->isClass()->getVtbl() && - "vtbl initializer type mismatch"); - +LLConstant *IrAggr::getVtblInit() { + if (constVtbl) return constVtbl; -} -////////////////////////////////////////////////////////////////////////////// + IF_LOG Logger::println("Building vtbl initializer"); + LOG_SCOPE; -LLConstant * IrAggr::getClassInfoInit() -{ - if (constClassInfo) - return constClassInfo; - constClassInfo = DtoDefineClassInfo(aggrdecl->isClassDeclaration()); - return constClassInfo; -} + ClassDeclaration *cd = aggrdecl->isClassDeclaration(); + assert(cd && "not class"); -////////////////////////////////////////////////////////////////////////////// + std::vector constants; + constants.reserve(cd->vtbl.dim); -llvm::GlobalVariable * IrAggr::getInterfaceVtbl(BaseClass * b, bool new_instance, size_t interfaces_index) -{ - auto it = interfaceVtblMap.find(b->base); - if (it != interfaceVtblMap.end()) - return it->second; + // start with the classinfo + llvm::Constant *c; + if (!cd->isCPPclass()) { + c = getClassInfoSymbol(); + c = DtoBitCast(c, DtoType(Type::typeinfoclass->type)); + constants.push_back(c); + } - IF_LOG Logger::println("Building vtbl for implementation of interface %s in class %s", - b->base->toPrettyChars(), aggrdecl->toPrettyChars()); - LOG_SCOPE; + // add virtual function pointers + size_t n = cd->vtbl.dim; + for (size_t i = cd->vtblOffset(); i < n; i++) { + Dsymbol *dsym = static_cast(cd->vtbl.data[i]); + assert(dsym && "null vtbl member"); - ClassDeclaration* cd = aggrdecl->isClassDeclaration(); - assert(cd && "not a class aggregate"); + FuncDeclaration *fd = dsym->isFuncDeclaration(); + assert(fd && "vtbl entry not a function"); - FuncDeclarations vtbl_array; - b->fillVtbl(cd, &vtbl_array, new_instance); - - std::vector constants; - constants.reserve(vtbl_array.dim); - - if (!b->base->isCPPinterface()) { // skip interface info for CPP interfaces - // index into the interfaces array - llvm::Constant* idxs[2] = { - DtoConstSize_t(0), - DtoConstSize_t(interfaces_index) - }; - - llvm::GlobalVariable* interfaceInfosZ = getInterfaceArraySymbol(); - llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr( -#if LDC_LLVM_VER >= 307 - isaPointer(interfaceInfosZ)->getElementType(), -#endif - interfaceInfosZ, idxs, true); - - constants.push_back(c); - } - - // add virtual function pointers - size_t n = vtbl_array.dim; - for (size_t i = b->base->vtblOffset(); i < n; i++) - { - Dsymbol* dsym = static_cast(vtbl_array.data[i]); - if (dsym == NULL) - { - // FIXME - // why is this null? - // happens for mini/s.d - constants.push_back(getNullValue(getVoidPtrType())); + if (cd->isAbstract() || (fd->isAbstract() && !fd->fbody)) { + c = getNullValue(getPtrToType(DtoFunctionType(fd))); + } else { + DtoResolveFunction(fd); + assert(isIrFuncCreated(fd) && "invalid vtbl function"); + c = getIrFunc(fd)->func; + if (cd->isFuncHidden( + fd)) { /* fd is hidden from the view of this class. + * If fd overlaps with any function in the vtbl[], then + * issue 'hidden' error. + */ + for (size_t j = 1; j < n; j++) { + if (j == i) continue; - } + FuncDeclaration *fd2 = + static_cast(cd->vtbl.data[j])->isFuncDeclaration(); + if (!fd2->ident->equals(fd->ident)) + continue; + if (fd->leastAsSpecialized(fd2) || fd2->leastAsSpecialized(fd)) { + TypeFunction *tf = static_cast(fd->type); + if (tf->ty == Tfunction) { + cd->error("use of %s%s is hidden by %s; use 'alias %s = %s.%s;' " + "to introduce base class overload set", + fd->toPrettyChars(), + parametersTypeToChars(tf->parameters, tf->varargs), + cd->toChars(), - FuncDeclaration* fd = dsym->isFuncDeclaration(); - assert(fd && "vtbl entry not a function"); - - assert((!fd->isAbstract() || fd->fbody) && - "null symbol in interface implementation vtable"); - - DtoResolveFunction(fd); - assert(isIrFuncCreated(fd) && "invalid vtbl function"); - - IrFunction *irFunc = getIrFunc(fd); - - assert(irFunc->irFty.arg_this); - - // Create the thunk function if it does not already exist in this - // module. - OutBuffer nameBuf; - nameBuf.writestring("Th"); - nameBuf.printf("%i", b->offset); - nameBuf.writestring(mangleExact(fd)); - const char *thunkName = nameBuf.extractString(); - llvm::Function *thunk = gIR->module.getFunction(thunkName); - if (!thunk) - { - thunk = LLFunction::Create( - isaFunction(irFunc->func->getType()->getContainedType(0)), - llvm::GlobalValue::LinkOnceODRLinkage, thunkName, - &gIR->module); - SET_COMDAT(thunk, gIR->module); - thunk->copyAttributesFrom(irFunc->func); - - // Thunks themselves don't have an identity, only the target - // function has. - thunk->setUnnamedAddr(true); - - // create entry and end blocks - llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(gIR->context(), "", thunk); - gIR->scopes.push_back(IRScope(beginbb)); - - // Copy the function parameters, so later we can pass them to the - // real function and set their names from the original function (the - // latter being just for IR readablilty). - std::vector args; - llvm::Function::arg_iterator thunkArg = thunk->arg_begin(); - llvm::Function::arg_iterator origArg = irFunc->func->arg_begin(); - for (; thunkArg != thunk->arg_end(); ++thunkArg, ++origArg) - { - thunkArg->setName(origArg->getName()); - args.push_back(thunkArg); + fd->toChars(), fd->parent->toChars(), fd->toChars()); + } else { + cd->error("use of %s is hidden by %s", fd->toPrettyChars(), + cd->toChars()); } - - // cast 'this' to Object - LLValue* &thisArg = args[(!irFunc->irFty.arg_sret || gABI->passThisBeforeSret(irFunc->type)) ? 0 : 1]; - LLType* targetThisType = thisArg->getType(); - thisArg = DtoBitCast(thisArg, getVoidPtrType()); - thisArg = DtoGEP1(thisArg, DtoConstInt(-b->offset)); - thisArg = DtoBitCast(thisArg, targetThisType); - - // call the real vtbl function. - llvm::CallSite call = gIR->ir->CreateCall(irFunc->func, args); - call.setCallingConv(irFunc->func->getCallingConv()); - - // return from the thunk - if (thunk->getReturnType() == LLType::getVoidTy(gIR->context())) - llvm::ReturnInst::Create(gIR->context(), beginbb); - else - llvm::ReturnInst::Create(gIR->context(), call.getInstruction(), beginbb); - - // clean up - gIR->scopes.pop_back(); + fatal(); + break; + } } - - constants.push_back(thunk); + } } + constants.push_back(c); + } - // build the vtbl constant - llvm::Constant* vtbl_constant = LLConstantStruct::getAnon(gIR->context(), constants, false); + // build the constant struct + LLType *vtblTy = stripModifiers(type)->ctype->isClass()->getVtbl(); +#ifndef NDEBUG + size_t nc = constants.size(); - std::string mangledName("_D"); - mangledName.append(mangle(cd)); - mangledName.append("11__interface"); - mangledName.append(mangle(b->base)); - mangledName.append("6__vtblZ"); + for (size_t i = 0; i < nc; ++i) { + if (constants[i]->getType() != vtblTy->getContainedType(i)) { + llvm::errs() << "type mismatch for entry # " << i + << " in vtbl initializer\n"; - const LinkageWithCOMDAT lwc = DtoLinkage(cd); - llvm::GlobalVariable* GV = getOrCreateGlobal(cd->loc, - gIR->module, - vtbl_constant->getType(), - true, - lwc.first, - vtbl_constant, - mangledName - ); - if (lwc.second) SET_COMDAT(GV, gIR->module); - - // insert into the vtbl map - interfaceVtblMap.insert(std::make_pair(b->base, GV)); - - return GV; -} - -bool IrAggr::isPacked() const -{ - return static_cast(type->ctype)->packed; -} - -////////////////////////////////////////////////////////////////////////////// - -LLConstant * IrAggr::getClassInfoInterfaces() -{ - IF_LOG Logger::println("Building ClassInfo.interfaces"); - LOG_SCOPE; - - ClassDeclaration* cd = aggrdecl->isClassDeclaration(); - assert(cd); - - size_t n = interfacesWithVtbls.size(); - assert(stripModifiers(type)->ctype->isClass()->getNumInterfaceVtbls() == n && - "inconsistent number of interface vtables in this class"); - - VarDeclaration *interfaces_idx = Type::typeinfoclass->fields[3]; - - if (n == 0) - return getNullValue(DtoType(interfaces_idx->type)); - - // Build array of: - // - // struct Interface - // { - // ClassInfo classinfo; - // void*[] vtbl; - // ptrdiff_t offset; - // } - - LLSmallVector constants; - constants.reserve(cd->vtblInterfaces->dim); - - LLType* classinfo_type = DtoType(Type::typeinfoclass->type); - LLType* voidptrptr_type = DtoType( - Type::tvoid->pointerTo()->pointerTo()); - VarDeclaration *idx = Type::typeinfoclass->fields[3]; - LLStructType* interface_type = isaStruct(DtoType(idx->type->nextOf())); - assert(interface_type); - - for (size_t i = 0; i < n; ++i) - { - BaseClass* it = interfacesWithVtbls[i]; - - IF_LOG Logger::println("Adding interface %s", it->base->toPrettyChars()); - - IrAggr* irinter = getIrAggr(it->base); - assert(irinter && "interface has null IrStruct"); - IrTypeClass* itc = stripModifiers(irinter->type)->ctype->isClass(); - assert(itc && "null interface IrTypeClass"); - - // classinfo - LLConstant* ci = irinter->getClassInfoSymbol(); - ci = DtoBitCast(ci, classinfo_type); - - // vtbl - LLConstant* vtb; - // interface get a null - if (cd->isInterfaceDeclaration()) - { - vtb = DtoConstSlice(DtoConstSize_t(0), getNullValue(voidptrptr_type)); - } - else - { - auto itv = interfaceVtblMap.find(it->base); - assert(itv != interfaceVtblMap.end() && "interface vtbl not found"); - vtb = itv->second; - vtb = DtoBitCast(vtb, voidptrptr_type); - vtb = DtoConstSlice(DtoConstSize_t(itc->getVtblSize()), vtb); - } - - // offset - LLConstant* off = DtoConstSize_t(it->offset); - - // create Interface struct - LLConstant* inits[3] = { ci, vtb, off }; - LLConstant* entry = LLConstantStruct::get(interface_type, llvm::makeArrayRef(inits, 3)); - constants.push_back(entry); + constants[i]->getType()->dump(); + vtblTy->getContainedType(i)->dump(); } + } - // create Interface[N] - LLArrayType* array_type = llvm::ArrayType::get(interface_type, n); - - // create and apply initializer - LLConstant* arr = LLConstantArray::get(array_type, constants); - classInterfacesArray->setInitializer(arr); - const LinkageWithCOMDAT lwc = DtoLinkage(cd); - classInterfacesArray->setLinkage(lwc.first); - if (lwc.second) SET_COMDAT(classInterfacesArray, gIR->module); - - // return null, only baseclass provide interfaces - if (cd->vtblInterfaces->dim == 0) - { - return getNullValue(DtoType(interfaces_idx->type)); - } - - // only the interface explicitly implemented by this class - // (not super classes) should show in ClassInfo - LLConstant* idxs[2] = { - DtoConstSize_t(0), - DtoConstSize_t(n - cd->vtblInterfaces->dim) - }; - - LLConstant* ptr = llvm::ConstantExpr::getGetElementPtr( -#if LDC_LLVM_VER >= 307 - isaPointer(classInterfacesArray)->getElementType(), #endif - classInterfacesArray, idxs, true); + constVtbl = LLConstantStruct::get(isaStruct(vtblTy), constants); - // return as a slice - return DtoConstSlice( DtoConstSize_t(cd->vtblInterfaces->dim), ptr ); + assert(constVtbl->getType() == + stripModifiers(type)->ctype->isClass()->getVtbl() && + "vtbl initializer type mismatch"); + + return constVtbl; } ////////////////////////////////////////////////////////////////////////////// -void IrAggr::initializeInterface() -{ - InterfaceDeclaration* base = aggrdecl->isInterfaceDeclaration(); - assert(base && "not interface"); +LLConstant *IrAggr::getClassInfoInit() { + if (constClassInfo) + return constClassInfo; + constClassInfo = DtoDefineClassInfo(aggrdecl->isClassDeclaration()); + return constClassInfo; +} - // has interface vtbls? - if (!base->vtblInterfaces) - return; +////////////////////////////////////////////////////////////////////////////// - for (auto bc : *base->vtblInterfaces) - { - // add to the interface list - interfacesWithVtbls.push_back(bc); +llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance, + size_t interfaces_index) { + auto it = interfaceVtblMap.find(b->base); + if (it != interfaceVtblMap.end()) + return it->second; + + IF_LOG Logger::println( + "Building vtbl for implementation of interface %s in class %s", + b->base->toPrettyChars(), aggrdecl->toPrettyChars()); + LOG_SCOPE; + + ClassDeclaration *cd = aggrdecl->isClassDeclaration(); + assert(cd && "not a class aggregate"); + + FuncDeclarations vtbl_array; + b->fillVtbl(cd, &vtbl_array, new_instance); + + std::vector constants; + constants.reserve(vtbl_array.dim); + + if (!b->base->isCPPinterface()) { // skip interface info for CPP interfaces + // index into the interfaces array + llvm::Constant *idxs[2] = {DtoConstSize_t(0), + DtoConstSize_t(interfaces_index)}; + + llvm::GlobalVariable *interfaceInfosZ = getInterfaceArraySymbol(); + llvm::Constant *c = llvm::ConstantExpr::getGetElementPtr( +#if LDC_LLVM_VER >= 307 + isaPointer(interfaceInfosZ)->getElementType(), +#endif + interfaceInfosZ, idxs, true); + + constants.push_back(c); + } + + // add virtual function pointers + size_t n = vtbl_array.dim; + for (size_t i = b->base->vtblOffset(); i < n; i++) { + Dsymbol *dsym = static_cast(vtbl_array.data[i]); + if (dsym == NULL) { + // FIXME + // why is this null? + // happens for mini/s.d + constants.push_back(getNullValue(getVoidPtrType())); + continue; } + + FuncDeclaration *fd = dsym->isFuncDeclaration(); + assert(fd && "vtbl entry not a function"); + + assert((!fd->isAbstract() || fd->fbody) && + "null symbol in interface implementation vtable"); + + DtoResolveFunction(fd); + assert(isIrFuncCreated(fd) && "invalid vtbl function"); + + IrFunction *irFunc = getIrFunc(fd); + + assert(irFunc->irFty.arg_this); + + // Create the thunk function if it does not already exist in this + // module. + OutBuffer nameBuf; + nameBuf.writestring("Th"); + nameBuf.printf("%i", b->offset); + nameBuf.writestring(mangleExact(fd)); + const char *thunkName = nameBuf.extractString(); + llvm::Function *thunk = gIR->module.getFunction(thunkName); + if (!thunk) { + thunk = LLFunction::Create( + isaFunction(irFunc->func->getType()->getContainedType(0)), + llvm::GlobalValue::LinkOnceODRLinkage, thunkName, &gIR->module); + SET_COMDAT(thunk, gIR->module); + thunk->copyAttributesFrom(irFunc->func); + + // Thunks themselves don't have an identity, only the target + // function has. + thunk->setUnnamedAddr(true); + + // create entry and end blocks + llvm::BasicBlock *beginbb = + llvm::BasicBlock::Create(gIR->context(), "", thunk); + gIR->scopes.push_back(IRScope(beginbb)); + + // Copy the function parameters, so later we can pass them to the + // real function and set their names from the original function (the + // latter being just for IR readablilty). + std::vector args; + llvm::Function::arg_iterator thunkArg = thunk->arg_begin(); + llvm::Function::arg_iterator origArg = irFunc->func->arg_begin(); + for (; thunkArg != thunk->arg_end(); ++thunkArg, ++origArg) { + thunkArg->setName(origArg->getName()); + args.push_back(thunkArg); + } + + // cast 'this' to Object + LLValue *&thisArg = args[(!irFunc->irFty.arg_sret || + gABI->passThisBeforeSret(irFunc->type)) + ? 0 + : 1]; + LLType *targetThisType = thisArg->getType(); + thisArg = DtoBitCast(thisArg, getVoidPtrType()); + thisArg = DtoGEP1(thisArg, DtoConstInt(-b->offset)); + thisArg = DtoBitCast(thisArg, targetThisType); + + // call the real vtbl function. + llvm::CallSite call = gIR->ir->CreateCall(irFunc->func, args); + call.setCallingConv(irFunc->func->getCallingConv()); + + // return from the thunk + if (thunk->getReturnType() == LLType::getVoidTy(gIR->context())) + llvm::ReturnInst::Create(gIR->context(), beginbb); + else + llvm::ReturnInst::Create(gIR->context(), call.getInstruction(), + beginbb); + + // clean up + gIR->scopes.pop_back(); + } + + constants.push_back(thunk); + } + + // build the vtbl constant + llvm::Constant *vtbl_constant = + LLConstantStruct::getAnon(gIR->context(), constants, false); + + std::string mangledName("_D"); + mangledName.append(mangle(cd)); + mangledName.append("11__interface"); + mangledName.append(mangle(b->base)); + mangledName.append("6__vtblZ"); + + const LinkageWithCOMDAT lwc = DtoLinkage(cd); + llvm::GlobalVariable *GV = + getOrCreateGlobal(cd->loc, gIR->module, vtbl_constant->getType(), true, + lwc.first, vtbl_constant, mangledName); + if (lwc.second) + SET_COMDAT(GV, gIR->module); + + // insert into the vtbl map + interfaceVtblMap.insert(std::make_pair(b->base, GV)); + + return GV; +} + +bool IrAggr::isPacked() const { + return static_cast(type->ctype)->packed; +} + +////////////////////////////////////////////////////////////////////////////// + +LLConstant *IrAggr::getClassInfoInterfaces() { + IF_LOG Logger::println("Building ClassInfo.interfaces"); + LOG_SCOPE; + + ClassDeclaration *cd = aggrdecl->isClassDeclaration(); + assert(cd); + + size_t n = interfacesWithVtbls.size(); + assert(stripModifiers(type)->ctype->isClass()->getNumInterfaceVtbls() == n && + "inconsistent number of interface vtables in this class"); + + VarDeclaration *interfaces_idx = Type::typeinfoclass->fields[3]; + + if (n == 0) + return getNullValue(DtoType(interfaces_idx->type)); + + // Build array of: + // + // struct Interface + // { + // ClassInfo classinfo; + // void*[] vtbl; + // ptrdiff_t offset; + // } + + LLSmallVector constants; + constants.reserve(cd->vtblInterfaces->dim); + + LLType *classinfo_type = DtoType(Type::typeinfoclass->type); + LLType *voidptrptr_type = DtoType(Type::tvoid->pointerTo()->pointerTo()); + VarDeclaration *idx = Type::typeinfoclass->fields[3]; + LLStructType *interface_type = isaStruct(DtoType(idx->type->nextOf())); + assert(interface_type); + + for (size_t i = 0; i < n; ++i) { + BaseClass *it = interfacesWithVtbls[i]; + + IF_LOG Logger::println("Adding interface %s", it->base->toPrettyChars()); + + IrAggr *irinter = getIrAggr(it->base); + assert(irinter && "interface has null IrStruct"); + IrTypeClass *itc = stripModifiers(irinter->type)->ctype->isClass(); + assert(itc && "null interface IrTypeClass"); + + // classinfo + LLConstant *ci = irinter->getClassInfoSymbol(); + ci = DtoBitCast(ci, classinfo_type); + + // vtbl + LLConstant *vtb; + // interface get a null + if (cd->isInterfaceDeclaration()) { + vtb = DtoConstSlice(DtoConstSize_t(0), getNullValue(voidptrptr_type)); + } else { + auto itv = interfaceVtblMap.find(it->base); + assert(itv != interfaceVtblMap.end() && "interface vtbl not found"); + vtb = itv->second; + vtb = DtoBitCast(vtb, voidptrptr_type); + vtb = DtoConstSlice(DtoConstSize_t(itc->getVtblSize()), vtb); + } + + // offset + LLConstant *off = DtoConstSize_t(it->offset); + + // create Interface struct + LLConstant *inits[3] = {ci, vtb, off}; + LLConstant *entry = + LLConstantStruct::get(interface_type, llvm::makeArrayRef(inits, 3)); + constants.push_back(entry); + } + + // create Interface[N] + LLArrayType *array_type = llvm::ArrayType::get(interface_type, n); + + // create and apply initializer + LLConstant *arr = LLConstantArray::get(array_type, constants); + classInterfacesArray->setInitializer(arr); + const LinkageWithCOMDAT lwc = DtoLinkage(cd); + classInterfacesArray->setLinkage(lwc.first); + if (lwc.second) + SET_COMDAT(classInterfacesArray, gIR->module); + + // return null, only baseclass provide interfaces + if (cd->vtblInterfaces->dim == 0) { + return getNullValue(DtoType(interfaces_idx->type)); + } + + // only the interface explicitly implemented by this class + // (not super classes) should show in ClassInfo + LLConstant *idxs[2] = {DtoConstSize_t(0), + DtoConstSize_t(n - cd->vtblInterfaces->dim)}; + + LLConstant *ptr = llvm::ConstantExpr::getGetElementPtr( +#if LDC_LLVM_VER >= 307 + isaPointer(classInterfacesArray)->getElementType(), +#endif + classInterfacesArray, idxs, true); + + // return as a slice + return DtoConstSlice(DtoConstSize_t(cd->vtblInterfaces->dim), ptr); +} + +////////////////////////////////////////////////////////////////////////////// + +void IrAggr::initializeInterface() { + InterfaceDeclaration *base = aggrdecl->isInterfaceDeclaration(); + assert(base && "not interface"); + + // has interface vtbls? + if (!base->vtblInterfaces) + return; + + for (auto bc : *base->vtblInterfaces) { + // add to the interface list + interfacesWithVtbls.push_back(bc); + } } ////////////////////////////////////////////////////////////////////////////// diff --git a/ir/irdsymbol.cpp b/ir/irdsymbol.cpp index e674b3f9eb..4bf31b2de7 100644 --- a/ir/irdsymbol.cpp +++ b/ir/irdsymbol.cpp @@ -12,69 +12,58 @@ #include "ir/irdsymbol.h" #include "ir/irvar.h" -std::vector IrDsymbol::list; +std::vector IrDsymbol::list; -void IrDsymbol::resetAll() -{ - Logger::println("resetting %llu Dsymbols", static_cast(list.size())); +void IrDsymbol::resetAll() { + Logger::println("resetting %llu Dsymbols", + static_cast(list.size())); - for (auto s : list) - s->reset(); + for (auto s : list) + s->reset(); } -IrDsymbol::IrDsymbol() -{ - list.push_back(this); +IrDsymbol::IrDsymbol() { list.push_back(this); } + +IrDsymbol::IrDsymbol(const IrDsymbol &s) { + list.push_back(this); + irData = s.irData; + m_type = s.m_type; + m_state = s.m_state; } -IrDsymbol::IrDsymbol(const IrDsymbol& s) -{ - list.push_back(this); - irData = s.irData; - m_type = s.m_type; - m_state = s.m_state; +IrDsymbol::~IrDsymbol() { + if (this == list.back()) { + list.pop_back(); + return; + } + + auto it = std::find(list.rbegin(), list.rend(), this).base(); + // base() returns the iterator _after_ the found position + list.erase(--it); } -IrDsymbol::~IrDsymbol() -{ - if (this == list.back()) - { - list.pop_back(); - return; - } - - auto it = std::find(list.rbegin(), list.rend(), this).base(); - // base() returns the iterator _after_ the found position - list.erase(--it); +void IrDsymbol::reset() { + irData = nullptr; + m_type = Type::NotSet; + m_state = State::Initial; } -void IrDsymbol::reset() -{ - irData = nullptr; - m_type = Type::NotSet; - m_state = State::Initial; +void IrDsymbol::setResolved() { + if (m_state < Resolved) + m_state = Resolved; } -void IrDsymbol::setResolved() -{ - if (m_state < Resolved) - m_state = Resolved; +void IrDsymbol::setDeclared() { + if (m_state < Declared) + m_state = Declared; } -void IrDsymbol::setDeclared() -{ - if (m_state < Declared) - m_state = Declared; +void IrDsymbol::setInitialized() { + if (m_state < Initialized) + m_state = Initialized; } -void IrDsymbol::setInitialized() -{ - if (m_state < Initialized) - m_state = Initialized; -} - -void IrDsymbol::setDefined() -{ - if (m_state < Defined) - m_state = Defined; +void IrDsymbol::setDefined() { + if (m_state < Defined) + m_state = Defined; } diff --git a/ir/irdsymbol.h b/ir/irdsymbol.h index ada8b53efc..b277210f2b 100644 --- a/ir/irdsymbol.h +++ b/ir/irdsymbol.h @@ -31,78 +31,70 @@ class VarDeclaration; class Module; namespace llvm { - class Value; +class Value; } -struct IrDsymbol -{ - enum Type - { - NotSet, - ModuleType, - AggrType, - FuncType, - GlobalType, - LocalType, - ParamterType, - FieldType - }; +struct IrDsymbol { + enum Type { + NotSet, + ModuleType, + AggrType, + FuncType, + GlobalType, + LocalType, + ParamterType, + FieldType + }; - enum State - { - Initial, - Resolved, - Declared, - Initialized, - Defined - }; + enum State { Initial, Resolved, Declared, Initialized, Defined }; - static std::vector list; - static void resetAll(); + static std::vector list; + static void resetAll(); - // overload all of these to make sure - // the static list is up to date - IrDsymbol(); - IrDsymbol(const IrDsymbol& s); - ~IrDsymbol(); + // overload all of these to make sure + // the static list is up to date + IrDsymbol(); + IrDsymbol(const IrDsymbol &s); + ~IrDsymbol(); - void reset(); + void reset(); - Type type() const { return m_type; } - State state() const { return m_state; } + Type type() const { return m_type; } + State state() const { return m_state; } - bool isResolved() const { return m_state >= Resolved; } - bool isDeclared() const { return m_state >= Declared; } - bool isInitialized() const { return m_state >= Initialized; } - bool isDefined() const { return m_state >= Defined; } + bool isResolved() const { return m_state >= Resolved; } + bool isDeclared() const { return m_state >= Declared; } + bool isInitialized() const { return m_state >= Initialized; } + bool isDefined() const { return m_state >= Defined; } + + void setResolved(); + void setDeclared(); + void setInitialized(); + void setDefined(); - void setResolved(); - void setDeclared(); - void setInitialized(); - void setDefined(); private: - friend IrModule* getIrModule(Module *m); - friend IrAggr *getIrAggr(AggregateDeclaration *decl, bool create); - friend IrFunction *getIrFunc(FuncDeclaration *decl, bool create); - friend IrVar *getIrVar(VarDeclaration *decl); - friend IrGlobal *getIrGlobal(VarDeclaration *decl, bool create); - friend IrLocal *getIrLocal(VarDeclaration *decl, bool create); - friend IrParameter *getIrParameter(VarDeclaration *decl, bool create); - friend IrField *getIrField(VarDeclaration *decl, bool create); + friend IrModule *getIrModule(Module *m); + friend IrAggr *getIrAggr(AggregateDeclaration *decl, bool create); + friend IrFunction *getIrFunc(FuncDeclaration *decl, bool create); + friend IrVar *getIrVar(VarDeclaration *decl); + friend IrGlobal *getIrGlobal(VarDeclaration *decl, bool create); + friend IrLocal *getIrLocal(VarDeclaration *decl, bool create); + friend IrParameter *getIrParameter(VarDeclaration *decl, bool create); + friend IrField *getIrField(VarDeclaration *decl, bool create); - union { - void* irData = nullptr; - IrModule* irModule; - IrAggr* irAggr; - IrFunction* irFunc; - IrVar* irVar; - IrGlobal* irGlobal; - IrLocal* irLocal; - IrParameter* irParam; - IrField* irField; - }; - Type m_type = Type::NotSet; - State m_state = State::Initial; + union { + void *irData = nullptr; + IrModule *irModule; + IrAggr *irAggr; + IrFunction *irFunc; + IrVar *irVar; + IrGlobal *irGlobal; + IrLocal *irLocal; + IrParameter *irParam; + IrField *irField; + }; + Type m_type = Type::NotSet; + State m_state = State::Initial; }; #endif diff --git a/ir/irforw.h b/ir/irforw.h index 0d05ee4f94..b34b0e95c8 100644 --- a/ir/irforw.h +++ b/ir/irforw.h @@ -11,7 +11,6 @@ // //===----------------------------------------------------------------------===// - #ifndef LDC_IR_IRFORW_H #define LDC_IR_IRFORW_H @@ -38,22 +37,21 @@ struct TypeArray; class TypeFunction; // llvm forward declarations -namespace llvm -{ - class Value; - class GlobalValue; - class GlobalVariable; - class Function; - class Constant; - class ConstantStruct; - class ConstantArray; - class DataLayout; - class Type; - class StructType; - class ArrayType; - class PointerType; - class BasicBlock; - class Instruction; +namespace llvm { +class Value; +class GlobalValue; +class GlobalVariable; +class Function; +class Constant; +class ConstantStruct; +class ConstantArray; +class DataLayout; +class Type; +class StructType; +class ArrayType; +class PointerType; +class BasicBlock; +class Instruction; } #endif diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index 1f877d3fe9..4b5051a167 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -16,443 +16,428 @@ #include "ir/irfunction.h" #include -JumpTarget::JumpTarget(llvm::BasicBlock* targetBlock, CleanupCursor cleanupScope, - Statement* targetStatement) +JumpTarget::JumpTarget(llvm::BasicBlock *targetBlock, + CleanupCursor cleanupScope, Statement *targetStatement) : targetBlock(targetBlock), cleanupScope(cleanupScope), - targetStatement(targetStatement) -{} + targetStatement(targetStatement) {} -GotoJump::GotoJump(Loc loc, llvm::BasicBlock* sourceBlock, - llvm::BasicBlock* tentativeTarget, Identifier* targetLabel) +GotoJump::GotoJump(Loc loc, llvm::BasicBlock *sourceBlock, + llvm::BasicBlock *tentativeTarget, Identifier *targetLabel) : sourceLoc(loc), sourceBlock(sourceBlock), - tentativeTarget(tentativeTarget), targetLabel(targetLabel) -{} + tentativeTarget(tentativeTarget), targetLabel(targetLabel) {} -CatchScope::CatchScope(llvm::Constant* classInfoPtr, llvm::BasicBlock* bodyBlock, - CleanupCursor cleanupScope) - : classInfoPtr(classInfoPtr), bodyBlock(bodyBlock), cleanupScope(cleanupScope) -{} +CatchScope::CatchScope(llvm::Constant *classInfoPtr, + llvm::BasicBlock *bodyBlock, CleanupCursor cleanupScope) + : classInfoPtr(classInfoPtr), bodyBlock(bodyBlock), + cleanupScope(cleanupScope) {} namespace { -void executeCleanup(IRState* irs, CleanupScope& scope, - llvm::BasicBlock* sourceBlock, llvm::BasicBlock* continueWith -) { - if (scope.exitTargets.empty() || ( - scope.exitTargets.size() == 1 && - scope.exitTargets[0].branchTarget == continueWith - )) { - // We didn't need a branch selector before and still don't need one. - assert(!scope.branchSelector); +void executeCleanup(IRState *irs, CleanupScope &scope, + llvm::BasicBlock *sourceBlock, + llvm::BasicBlock *continueWith) { + if (scope.exitTargets.empty() || + (scope.exitTargets.size() == 1 && + scope.exitTargets[0].branchTarget == continueWith)) { + // We didn't need a branch selector before and still don't need one. + assert(!scope.branchSelector); - // Set up the unconditional branch at the end of the cleanup if we have - // not done so already. - if (scope.exitTargets.empty()) { - scope.exitTargets.push_back(CleanupExitTarget(continueWith)); - llvm::BranchInst::Create(continueWith, scope.endBlock); - } - scope.exitTargets.front().sourceBlocks.push_back(sourceBlock); - return; + // Set up the unconditional branch at the end of the cleanup if we have + // not done so already. + if (scope.exitTargets.empty()) { + scope.exitTargets.push_back(CleanupExitTarget(continueWith)); + llvm::BranchInst::Create(continueWith, scope.endBlock); + } + scope.exitTargets.front().sourceBlocks.push_back(sourceBlock); + return; + } + + // We need a branch selector if we are here... + if (!scope.branchSelector) { + // ... and have not created one yet, so do so now. + scope.branchSelector = new llvm::AllocaInst( + llvm::Type::getInt32Ty(gIR->context()), + llvm::Twine("branchsel.") + scope.beginBlock->getName(), + irs->topallocapoint()); + + // Now we also need to store 0 to it to keep the paths that go to the + // only existing branch target the same. + auto &v = scope.exitTargets.front().sourceBlocks; + for (auto bb : v) { + new llvm::StoreInst(DtoConstUint(0), scope.branchSelector, + bb->getTerminator()); } - // We need a branch selector if we are here... - if (!scope.branchSelector) { - // ... and have not created one yet, so do so now. - scope.branchSelector = new llvm::AllocaInst( - llvm::Type::getInt32Ty(gIR->context()), - llvm::Twine("branchsel.") + scope.beginBlock->getName(), - irs->topallocapoint() - ); + // And convert the BranchInst to the existing branch target to a + // SelectInst so we can append the other cases to it. + scope.endBlock->getTerminator()->eraseFromParent(); + llvm::Value *sel = + new llvm::LoadInst(scope.branchSelector, "", scope.endBlock); + llvm::SwitchInst::Create( + sel, scope.exitTargets[0].branchTarget, + 1, // Expected number of branches, only for pre-allocating. + scope.endBlock); + } - // Now we also need to store 0 to it to keep the paths that go to the - // only existing branch target the same. - auto& v = scope.exitTargets.front().sourceBlocks; - for (auto bb : v) - { - new llvm::StoreInst(DtoConstUint(0), scope.branchSelector, - bb->getTerminator()); - } + // If we already know this branch target, figure out the branch selector + // value and simply insert the store into the source block (prior to the + // last instruction, which is the branch to the first cleanup). + for (unsigned i = 0; i < scope.exitTargets.size(); ++i) { + CleanupExitTarget &t = scope.exitTargets[i]; + if (t.branchTarget == continueWith) { + new llvm::StoreInst(DtoConstUint(i), scope.branchSelector, + sourceBlock->getTerminator()); - // And convert the BranchInst to the existing branch target to a - // SelectInst so we can append the other cases to it. - scope.endBlock->getTerminator()->eraseFromParent(); - llvm::Value* sel = new llvm::LoadInst(scope.branchSelector, "", - scope.endBlock); - llvm::SwitchInst::Create( - sel, - scope.exitTargets[0].branchTarget, - 1, // Expected number of branches, only for pre-allocating. - scope.endBlock - ); + // Note: Strictly speaking, keeping this up to date would not be + // needed right now, because we never to any optimizations that + // require changes to the source blocks after the initial conversion + // from one to two branch targets. Keeping this around for now to + // ease future development, but may be removed to save some work. + t.sourceBlocks.push_back(sourceBlock); + + return; } + } - // If we already know this branch target, figure out the branch selector - // value and simply insert the store into the source block (prior to the - // last instruction, which is the branch to the first cleanup). - for (unsigned i = 0; i < scope.exitTargets.size(); ++i) { - CleanupExitTarget& t = scope.exitTargets[i]; - if (t.branchTarget == continueWith) { - new llvm::StoreInst(DtoConstUint(i), scope.branchSelector, - sourceBlock->getTerminator()); + // We don't know this branch target yet, so add it to the SwitchInst... + llvm::ConstantInt *const selectorVal = DtoConstUint(scope.exitTargets.size()); + llvm::cast(scope.endBlock->getTerminator()) + ->addCase(selectorVal, continueWith); - // Note: Strictly speaking, keeping this up to date would not be - // needed right now, because we never to any optimizations that - // require changes to the source blocks after the initial conversion - // from one to two branch targets. Keeping this around for now to - // ease future development, but may be removed to save some work. - t.sourceBlocks.push_back(sourceBlock); + // ... insert the store into the source block... + new llvm::StoreInst(selectorVal, scope.branchSelector, + sourceBlock->getTerminator()); - return; - } - } - - // We don't know this branch target yet, so add it to the SwitchInst... - llvm::ConstantInt* const selectorVal = DtoConstUint(scope.exitTargets.size()); - llvm::cast(scope.endBlock->getTerminator())->addCase( - selectorVal, continueWith); - - // ... insert the store into the source block... - new llvm::StoreInst(selectorVal, scope.branchSelector, - sourceBlock->getTerminator()); - - // ... and keep track of it (again, this is unnecessary right now as - // discussed in the above note). - scope.exitTargets.push_back(CleanupExitTarget(continueWith)); - scope.exitTargets.back().sourceBlocks.push_back(sourceBlock); + // ... and keep track of it (again, this is unnecessary right now as + // discussed in the above note). + scope.exitTargets.push_back(CleanupExitTarget(continueWith)); + scope.exitTargets.back().sourceBlocks.push_back(sourceBlock); } } ScopeStack::~ScopeStack() { - // If there are still unresolved gotos left, it means that they were either - // down or "sideways" (i.e. down another branch) of the tree of all - // cleanup scopes, both of which are not allowed in D. - if (!topLevelUnresolvedGotos.empty()) { - for (const auto& i : topLevelUnresolvedGotos) - error(i.sourceLoc, "goto into try/finally scope is not allowed"); - fatal(); - } + // If there are still unresolved gotos left, it means that they were either + // down or "sideways" (i.e. down another branch) of the tree of all + // cleanup scopes, both of which are not allowed in D. + if (!topLevelUnresolvedGotos.empty()) { + for (const auto &i : topLevelUnresolvedGotos) + error(i.sourceLoc, "goto into try/finally scope is not allowed"); + fatal(); + } } -void ScopeStack::pushCleanup(llvm::BasicBlock* beginBlock, llvm::BasicBlock* endBlock) { - cleanupScopes.push_back(CleanupScope(beginBlock, endBlock)); +void ScopeStack::pushCleanup(llvm::BasicBlock *beginBlock, + llvm::BasicBlock *endBlock) { + cleanupScopes.push_back(CleanupScope(beginBlock, endBlock)); } -void ScopeStack::runCleanups( - CleanupCursor sourceScope, - CleanupCursor targetScope, - llvm::BasicBlock* continueWith -) { - assert(targetScope <= sourceScope); +void ScopeStack::runCleanups(CleanupCursor sourceScope, + CleanupCursor targetScope, + llvm::BasicBlock *continueWith) { + assert(targetScope <= sourceScope); - if (targetScope == sourceScope) { - // No cleanups to run, just branch to the next block. - irs->ir->CreateBr(continueWith); - return; - } + if (targetScope == sourceScope) { + // No cleanups to run, just branch to the next block. + irs->ir->CreateBr(continueWith); + return; + } - // Insert the unconditional branch to the first cleanup block. - irs->ir->CreateBr(cleanupScopes[sourceScope - 1].beginBlock); + // Insert the unconditional branch to the first cleanup block. + irs->ir->CreateBr(cleanupScopes[sourceScope - 1].beginBlock); - // Update all the control flow in the cleanups to make sure we end up where - // we want. - for (CleanupCursor i = sourceScope; i-- > targetScope;) { - llvm::BasicBlock* nextBlock = (i > targetScope) ? - cleanupScopes[i - 1].beginBlock : continueWith; - executeCleanup(irs, cleanupScopes[i], irs->scopebb(), nextBlock); - } + // Update all the control flow in the cleanups to make sure we end up where + // we want. + for (CleanupCursor i = sourceScope; i-- > targetScope;) { + llvm::BasicBlock *nextBlock = + (i > targetScope) ? cleanupScopes[i - 1].beginBlock : continueWith; + executeCleanup(irs, cleanupScopes[i], irs->scopebb(), nextBlock); + } } -void ScopeStack::runAllCleanups(llvm::BasicBlock* continueWith) { - runCleanups(0, continueWith); +void ScopeStack::runAllCleanups(llvm::BasicBlock *continueWith) { + runCleanups(0, continueWith); } void ScopeStack::popCleanups(CleanupCursor targetScope) { - assert(targetScope <= currentCleanupScope()); - if (targetScope == currentCleanupScope()) return; + assert(targetScope <= currentCleanupScope()); + if (targetScope == currentCleanupScope()) + return; - for (CleanupCursor i = currentCleanupScope(); i-- > targetScope;) { - // Any gotos that are still unresolved necessarily leave this scope. - // Thus, the cleanup needs to be executed. - for (const auto& gotoJump : currentUnresolvedGotos()) - { - // Make the source resp. last cleanup branch to this one. - llvm::BasicBlock* tentative = gotoJump.tentativeTarget; - tentative->replaceAllUsesWith(cleanupScopes[i].beginBlock); + for (CleanupCursor i = currentCleanupScope(); i-- > targetScope;) { + // Any gotos that are still unresolved necessarily leave this scope. + // Thus, the cleanup needs to be executed. + for (const auto &gotoJump : currentUnresolvedGotos()) { + // Make the source resp. last cleanup branch to this one. + llvm::BasicBlock *tentative = gotoJump.tentativeTarget; + tentative->replaceAllUsesWith(cleanupScopes[i].beginBlock); - // And continue execution with the tentative target (we simply reuse - // it because there is no reason not to). - executeCleanup(irs, cleanupScopes[i], gotoJump.sourceBlock, tentative); - } - - - std::vector& nextUnresolved = (i == 0) ? - topLevelUnresolvedGotos : cleanupScopes[i - 1].unresolvedGotos; - nextUnresolved.insert( - nextUnresolved.end(), - currentUnresolvedGotos().begin(), - currentUnresolvedGotos().end() - ); - - cleanupScopes.pop_back(); + // And continue execution with the tentative target (we simply reuse + // it because there is no reason not to). + executeCleanup(irs, cleanupScopes[i], gotoJump.sourceBlock, tentative); } + + std::vector &nextUnresolved = + (i == 0) ? topLevelUnresolvedGotos + : cleanupScopes[i - 1].unresolvedGotos; + nextUnresolved.insert(nextUnresolved.end(), + currentUnresolvedGotos().begin(), + currentUnresolvedGotos().end()); + + cleanupScopes.pop_back(); + } } -void ScopeStack::pushCatch(llvm::Constant* classInfoPtr, - llvm::BasicBlock* bodyBlock -) { - catchScopes.emplace_back(classInfoPtr, bodyBlock, currentCleanupScope()); - currentLandingPads().push_back(nullptr); +void ScopeStack::pushCatch(llvm::Constant *classInfoPtr, + llvm::BasicBlock *bodyBlock) { + catchScopes.emplace_back(classInfoPtr, bodyBlock, currentCleanupScope()); + currentLandingPads().push_back(nullptr); } void ScopeStack::popCatch() { - catchScopes.pop_back(); - currentLandingPads().pop_back(); + catchScopes.pop_back(); + currentLandingPads().pop_back(); } -void ScopeStack::pushLoopTarget(Statement* loopStatement, llvm::BasicBlock* continueTarget, - llvm::BasicBlock* breakTarget -) { - continueTargets.emplace_back(continueTarget, currentCleanupScope(), loopStatement); - breakTargets.emplace_back(breakTarget, currentCleanupScope(), loopStatement); +void ScopeStack::pushLoopTarget(Statement *loopStatement, + llvm::BasicBlock *continueTarget, + llvm::BasicBlock *breakTarget) { + continueTargets.emplace_back(continueTarget, currentCleanupScope(), + loopStatement); + breakTargets.emplace_back(breakTarget, currentCleanupScope(), loopStatement); } void ScopeStack::popLoopTarget() { - continueTargets.pop_back(); - breakTargets.pop_back(); + continueTargets.pop_back(); + breakTargets.pop_back(); } -void ScopeStack::pushBreakTarget(Statement* switchStatement, - llvm::BasicBlock* targetBlock -) { - breakTargets.push_back({targetBlock, currentCleanupScope(), switchStatement}); +void ScopeStack::pushBreakTarget(Statement *switchStatement, + llvm::BasicBlock *targetBlock) { + breakTargets.push_back({targetBlock, currentCleanupScope(), switchStatement}); } -void ScopeStack::popBreakTarget() { - breakTargets.pop_back(); -} +void ScopeStack::popBreakTarget() { breakTargets.pop_back(); } -void ScopeStack::addLabelTarget(Identifier* labelName, - llvm::BasicBlock* targetBlock -) { - labelTargets[labelName] = {targetBlock, currentCleanupScope(), 0}; +void ScopeStack::addLabelTarget(Identifier *labelName, + llvm::BasicBlock *targetBlock) { + labelTargets[labelName] = {targetBlock, currentCleanupScope(), 0}; - // See whether any of the unresolved gotos target this label, and resolve - // those that do. - std::vector& unresolved = currentUnresolvedGotos(); - size_t i = 0; - while (i < unresolved.size()) { - if (unresolved[i].targetLabel != labelName) { - ++i; - continue; - } - - unresolved[i].tentativeTarget->replaceAllUsesWith(targetBlock); - unresolved[i].tentativeTarget->eraseFromParent(); - unresolved.erase(unresolved.begin() + i); - } -} - -void ScopeStack::jumpToLabel(Loc loc, Identifier* labelName) { - // If we have already seen that label, branch to it, executing any cleanups - // as necessary. - auto it = labelTargets.find(labelName); - if (it != labelTargets.end()) { - runCleanups(it->second.cleanupScope, it->second.targetBlock); - return; + // See whether any of the unresolved gotos target this label, and resolve + // those that do. + std::vector &unresolved = currentUnresolvedGotos(); + size_t i = 0; + while (i < unresolved.size()) { + if (unresolved[i].targetLabel != labelName) { + ++i; + continue; } - llvm::BasicBlock* target = - llvm::BasicBlock::Create(irs->context(), "goto.unresolved", irs->topfunc()); - irs->ir->CreateBr(target); - currentUnresolvedGotos().emplace_back(loc, irs->scopebb(), target, labelName); + unresolved[i].tentativeTarget->replaceAllUsesWith(targetBlock); + unresolved[i].tentativeTarget->eraseFromParent(); + unresolved.erase(unresolved.begin() + i); + } } -void ScopeStack::jumpToStatement(std::vector& targets, - Statement* loopOrSwitchStatement -) { - for (std::vector::reverse_iterator it = targets.rbegin(), - end = targets.rend(); - it != end; ++it - ) { - if (it->targetStatement == loopOrSwitchStatement) { - runCleanups(it->cleanupScope, it->targetBlock); - return; - } +void ScopeStack::jumpToLabel(Loc loc, Identifier *labelName) { + // If we have already seen that label, branch to it, executing any cleanups + // as necessary. + auto it = labelTargets.find(labelName); + if (it != labelTargets.end()) { + runCleanups(it->second.cleanupScope, it->second.targetBlock); + return; + } + + llvm::BasicBlock *target = llvm::BasicBlock::Create( + irs->context(), "goto.unresolved", irs->topfunc()); + irs->ir->CreateBr(target); + currentUnresolvedGotos().emplace_back(loc, irs->scopebb(), target, labelName); +} + +void ScopeStack::jumpToStatement(std::vector &targets, + Statement *loopOrSwitchStatement) { + for (std::vector::reverse_iterator it = targets.rbegin(), + end = targets.rend(); + it != end; ++it) { + if (it->targetStatement == loopOrSwitchStatement) { + runCleanups(it->cleanupScope, it->targetBlock); + return; } - assert(false && "Target for labeled break not found."); + } + assert(false && "Target for labeled break not found."); } -void ScopeStack::jumpToClosest(std::vector& targets) { - assert(!targets.empty() && - "Encountered break/continue but no loop in scope."); - JumpTarget& t = targets.back(); - runCleanups(t.cleanupScope, t.targetBlock); +void ScopeStack::jumpToClosest(std::vector &targets) { + assert(!targets.empty() && + "Encountered break/continue but no loop in scope."); + JumpTarget &t = targets.back(); + runCleanups(t.cleanupScope, t.targetBlock); } -std::vector& ScopeStack::currentUnresolvedGotos() { - return cleanupScopes.empty() ? - topLevelUnresolvedGotos : - cleanupScopes.back().unresolvedGotos; +std::vector &ScopeStack::currentUnresolvedGotos() { + return cleanupScopes.empty() ? topLevelUnresolvedGotos + : cleanupScopes.back().unresolvedGotos; } -std::vector& ScopeStack::currentLandingPads() { - return cleanupScopes.empty() ? - topLevelLandingPads : - cleanupScopes.back().landingPads; +std::vector &ScopeStack::currentLandingPads() { + return cleanupScopes.empty() ? topLevelLandingPads + : cleanupScopes.back().landingPads; } namespace { -llvm::LandingPadInst* createLandingPadInst(IRState* irs) { - LLType* retType = LLStructType::get(LLType::getInt8PtrTy(irs->context()), - LLType::getInt32Ty(irs->context()), - nullptr); +llvm::LandingPadInst *createLandingPadInst(IRState *irs) { + LLType *retType = + LLStructType::get(LLType::getInt8PtrTy(irs->context()), + LLType::getInt32Ty(irs->context()), nullptr); #if LDC_LLVM_VER >= 307 - LLFunction* currentFunction = irs->func()->func; - if (!currentFunction->hasPersonalityFn()) { - LLFunction* personalityFn = LLVM_D_GetRuntimeFunction(Loc(), irs->module, "_d_eh_personality"); - currentFunction->setPersonalityFn(personalityFn); - } - return irs->ir->CreateLandingPad(retType, 0); + LLFunction *currentFunction = irs->func()->func; + if (!currentFunction->hasPersonalityFn()) { + LLFunction *personalityFn = + LLVM_D_GetRuntimeFunction(Loc(), irs->module, "_d_eh_personality"); + currentFunction->setPersonalityFn(personalityFn); + } + return irs->ir->CreateLandingPad(retType, 0); #else - LLFunction* personalityFn = LLVM_D_GetRuntimeFunction(Loc(), irs->module, "_d_eh_personality"); - return irs->ir->CreateLandingPad(retType, personalityFn, 0); + LLFunction *personalityFn = + LLVM_D_GetRuntimeFunction(Loc(), irs->module, "_d_eh_personality"); + return irs->ir->CreateLandingPad(retType, personalityFn, 0); #endif } } -llvm::BasicBlock* ScopeStack::emitLandingPad() { - // save and rewrite scope - IRScope savedIRScope = irs->scope(); +llvm::BasicBlock *ScopeStack::emitLandingPad() { + // save and rewrite scope + IRScope savedIRScope = irs->scope(); - llvm::BasicBlock* beginBB = llvm::BasicBlock::Create(irs->context(), - "landingPad", irs->topfunc()); - irs->scope() = IRScope(beginBB); + llvm::BasicBlock *beginBB = + llvm::BasicBlock::Create(irs->context(), "landingPad", irs->topfunc()); + irs->scope() = IRScope(beginBB); - llvm::LandingPadInst* landingPad = createLandingPadInst(irs); + llvm::LandingPadInst *landingPad = createLandingPadInst(irs); - // Stash away the exception object pointer and selector value into their - // stack slots. - llvm::Value* ehPtr = DtoExtractValue(landingPad, 0); - if (!irs->func()->resumeUnwindBlock) { - irs->func()->resumeUnwindBlock = llvm::BasicBlock::Create( - irs->context(), "unwind.resume", irs->topfunc()); + // Stash away the exception object pointer and selector value into their + // stack slots. + llvm::Value *ehPtr = DtoExtractValue(landingPad, 0); + if (!irs->func()->resumeUnwindBlock) { + irs->func()->resumeUnwindBlock = llvm::BasicBlock::Create( + irs->context(), "unwind.resume", irs->topfunc()); - llvm::BasicBlock* oldBB = irs->scopebb(); - irs->scope() = IRScope(irs->func()->resumeUnwindBlock); + llvm::BasicBlock *oldBB = irs->scopebb(); + irs->scope() = IRScope(irs->func()->resumeUnwindBlock); - llvm::Function* resumeFn = LLVM_D_GetRuntimeFunction(Loc(), - irs->module, "_d_eh_resume_unwind"); - irs->ir->CreateCall(resumeFn, - irs->ir->CreateLoad(irs->func()->getOrCreateEhPtrSlot())); - irs->ir->CreateUnreachable(); + llvm::Function *resumeFn = + LLVM_D_GetRuntimeFunction(Loc(), irs->module, "_d_eh_resume_unwind"); + irs->ir->CreateCall( + resumeFn, irs->ir->CreateLoad(irs->func()->getOrCreateEhPtrSlot())); + irs->ir->CreateUnreachable(); - irs->scope() = IRScope(oldBB); - } - irs->ir->CreateStore(ehPtr, irs->func()->getOrCreateEhPtrSlot()); + irs->scope() = IRScope(oldBB); + } + irs->ir->CreateStore(ehPtr, irs->func()->getOrCreateEhPtrSlot()); - llvm::Value* ehSelector = DtoExtractValue(landingPad, 1); - if (!irs->func()->ehSelectorSlot) { - irs->func()->ehSelectorSlot = - DtoRawAlloca(ehSelector->getType(), 0, "eh.selector"); - } - irs->ir->CreateStore(ehSelector, irs->func()->ehSelectorSlot); + llvm::Value *ehSelector = DtoExtractValue(landingPad, 1); + if (!irs->func()->ehSelectorSlot) { + irs->func()->ehSelectorSlot = + DtoRawAlloca(ehSelector->getType(), 0, "eh.selector"); + } + irs->ir->CreateStore(ehSelector, irs->func()->ehSelectorSlot); - // Add landingpad clauses, emit finallys and 'if' chain to catch the exception. - CleanupCursor lastCleanup = currentCleanupScope(); - for (auto it = catchScopes.rbegin(), end = catchScopes.rend(); it != end; ++it) { - // Insert any cleanups in between the last catch we ran and this one. - assert(lastCleanup >= it->cleanupScope); - if (lastCleanup > it->cleanupScope) { - landingPad->setCleanup(true); - llvm::BasicBlock* afterCleanupBB = llvm::BasicBlock::Create( - irs->context(), - beginBB->getName() + llvm::Twine(".after.cleanup"), - irs->topfunc() - ); - runCleanups(lastCleanup, it->cleanupScope, afterCleanupBB); - irs->scope() = IRScope(afterCleanupBB); - lastCleanup = it->cleanupScope; - } - - // Add the ClassInfo reference to the landingpad instruction so it is - // emitted to the EH tables. - landingPad->addClause(it->classInfoPtr); - - llvm::BasicBlock* mismatchBB = llvm::BasicBlock::Create( - irs->context(), - beginBB->getName() + llvm::Twine(".mismatch"), - irs->topfunc() - ); - - // "Call" llvm.eh.typeid.for, which gives us the eh selector value to compare with - llvm::Value* ehTypeId = irs->ir->CreateCall(GET_INTRINSIC_DECL(eh_typeid_for), - DtoBitCast(it->classInfoPtr, getVoidPtrType())); - - // Compare the selector value from the unwinder against the expected - // one and branch accordingly. - irs->ir->CreateCondBr( - irs->ir->CreateICmpEQ( - irs->ir->CreateLoad(irs->func()->ehSelectorSlot), ehTypeId), - it->bodyBlock, - mismatchBB - ); - irs->scope() = IRScope(mismatchBB); + // Add landingpad clauses, emit finallys and 'if' chain to catch the + // exception. + CleanupCursor lastCleanup = currentCleanupScope(); + for (auto it = catchScopes.rbegin(), end = catchScopes.rend(); it != end; + ++it) { + // Insert any cleanups in between the last catch we ran and this one. + assert(lastCleanup >= it->cleanupScope); + if (lastCleanup > it->cleanupScope) { + landingPad->setCleanup(true); + llvm::BasicBlock *afterCleanupBB = llvm::BasicBlock::Create( + irs->context(), beginBB->getName() + llvm::Twine(".after.cleanup"), + irs->topfunc()); + runCleanups(lastCleanup, it->cleanupScope, afterCleanupBB); + irs->scope() = IRScope(afterCleanupBB); + lastCleanup = it->cleanupScope; } - // No catch matched. Execute all finallys and resume unwinding. - if (lastCleanup > 0) { - landingPad->setCleanup(true); - runCleanups(lastCleanup, 0, irs->func()->resumeUnwindBlock); - } else if (!catchScopes.empty()) { - // Directly convert the last mismatch branch into a branch to the - // unwind resume block. - irs->scopebb()->replaceAllUsesWith(irs->func()->resumeUnwindBlock); - irs->scopebb()->eraseFromParent(); - } else { - irs->ir->CreateBr(irs->func()->resumeUnwindBlock); - } + // Add the ClassInfo reference to the landingpad instruction so it is + // emitted to the EH tables. + landingPad->addClause(it->classInfoPtr); - irs->scope() = savedIRScope; - return beginBB; + llvm::BasicBlock *mismatchBB = llvm::BasicBlock::Create( + irs->context(), beginBB->getName() + llvm::Twine(".mismatch"), + irs->topfunc()); + + // "Call" llvm.eh.typeid.for, which gives us the eh selector value to + // compare with + llvm::Value *ehTypeId = + irs->ir->CreateCall(GET_INTRINSIC_DECL(eh_typeid_for), + DtoBitCast(it->classInfoPtr, getVoidPtrType())); + + // Compare the selector value from the unwinder against the expected + // one and branch accordingly. + irs->ir->CreateCondBr( + irs->ir->CreateICmpEQ(irs->ir->CreateLoad(irs->func()->ehSelectorSlot), + ehTypeId), + it->bodyBlock, mismatchBB); + irs->scope() = IRScope(mismatchBB); + } + + // No catch matched. Execute all finallys and resume unwinding. + if (lastCleanup > 0) { + landingPad->setCleanup(true); + runCleanups(lastCleanup, 0, irs->func()->resumeUnwindBlock); + } else if (!catchScopes.empty()) { + // Directly convert the last mismatch branch into a branch to the + // unwind resume block. + irs->scopebb()->replaceAllUsesWith(irs->func()->resumeUnwindBlock); + irs->scopebb()->eraseFromParent(); + } else { + irs->ir->CreateBr(irs->func()->resumeUnwindBlock); + } + + irs->scope() = savedIRScope; + return beginBB; } -IrFunction::IrFunction(FuncDeclaration* fd) { - decl = fd; +IrFunction::IrFunction(FuncDeclaration *fd) { + decl = fd; - Type* t = fd->type->toBasetype(); - assert(t->ty == Tfunction); - type = static_cast(t); + Type *t = fd->type->toBasetype(); + assert(t->ty == Tfunction); + type = static_cast(t); } void IrFunction::setNeverInline() { - assert(!func->getAttributes().hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::AlwaysInline) && "function can't be never- and always-inline at the same time"); - func->addFnAttr(llvm::Attribute::NoInline); + assert(!func->getAttributes().hasAttribute(llvm::AttributeSet::FunctionIndex, + llvm::Attribute::AlwaysInline) && + "function can't be never- and always-inline at the same time"); + func->addFnAttr(llvm::Attribute::NoInline); } void IrFunction::setAlwaysInline() { - assert(!func->getAttributes().hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::NoInline) && "function can't be never- and always-inline at the same time"); - func->addFnAttr(llvm::Attribute::AlwaysInline); + assert(!func->getAttributes().hasAttribute(llvm::AttributeSet::FunctionIndex, + llvm::Attribute::NoInline) && + "function can't be never- and always-inline at the same time"); + func->addFnAttr(llvm::Attribute::AlwaysInline); } -llvm::AllocaInst* IrFunction::getOrCreateEhPtrSlot() { - if (!ehPtrSlot) { - ehPtrSlot = DtoRawAlloca(getVoidPtrType(), 0, "eh.ptr"); - } - return ehPtrSlot; +llvm::AllocaInst *IrFunction::getOrCreateEhPtrSlot() { + if (!ehPtrSlot) { + ehPtrSlot = DtoRawAlloca(getVoidPtrType(), 0, "eh.ptr"); + } + return ehPtrSlot; } -IrFunction* getIrFunc(FuncDeclaration* decl, bool create) -{ - if (!isIrFuncCreated(decl) && create) { - assert(decl->ir.irFunc == NULL); - decl->ir.irFunc = new IrFunction(decl); - decl->ir.m_type = IrDsymbol::FuncType; - } - assert(decl->ir.irFunc != NULL); - return decl->ir.irFunc; +IrFunction *getIrFunc(FuncDeclaration *decl, bool create) { + if (!isIrFuncCreated(decl) && create) { + assert(decl->ir.irFunc == NULL); + decl->ir.irFunc = new IrFunction(decl); + decl->ir.m_type = IrDsymbol::FuncType; + } + assert(decl->ir.irFunc != NULL); + return decl->ir.irFunc; } -bool isIrFuncCreated(FuncDeclaration* decl) { - int t = decl->ir.type(); - assert(t == IrDsymbol::FuncType || t == IrDsymbol::NotSet); - return t == IrDsymbol::FuncType; +bool isIrFuncCreated(FuncDeclaration *decl) { + int t = decl->ir.type(); + assert(t == IrDsymbol::FuncType || t == IrDsymbol::NotSet); + return t == IrDsymbol::FuncType; } diff --git a/ir/irfunction.h b/ir/irfunction.h index 41bb571b49..b809c620a9 100644 --- a/ir/irfunction.h +++ b/ir/irfunction.h @@ -12,7 +12,6 @@ // //===----------------------------------------------------------------------===// - #ifndef LDC_IR_IRFUNCTION_H #define LDC_IR_IRFUNCTION_H @@ -38,26 +37,26 @@ using CleanupCursor = size_t; /// Stores information needed to correctly jump to a given label or loop/switch /// statement (break/continue can be labeled, but are not necessarily). struct JumpTarget { - /// The basic block to ultimately branch to. - llvm::BasicBlock* targetBlock = nullptr; + /// The basic block to ultimately branch to. + llvm::BasicBlock *targetBlock = nullptr; - /// The index of the target in the stack of active cleanup scopes. - /// - /// When generating code for a jump to this label, the cleanups between - /// the current depth and that of the level will be emitted. Note that - /// we need to handle only one direction (towards the root of the stack) - /// because D forbids gotos into try or finally blocks. - // TODO: We might not be able to detect illegal jumps across try-finally - // blocks by only storing the index. - CleanupCursor cleanupScope; + /// The index of the target in the stack of active cleanup scopes. + /// + /// When generating code for a jump to this label, the cleanups between + /// the current depth and that of the level will be emitted. Note that + /// we need to handle only one direction (towards the root of the stack) + /// because D forbids gotos into try or finally blocks. + // TODO: We might not be able to detect illegal jumps across try-finally + // blocks by only storing the index. + CleanupCursor cleanupScope; - /// Keeps target of the associated loop or switch statement so we can - /// handle both unlabeled and labeled jumps. - Statement* targetStatement = nullptr; + /// Keeps target of the associated loop or switch statement so we can + /// handle both unlabeled and labeled jumps. + Statement *targetStatement = nullptr; - JumpTarget() = default; - JumpTarget(llvm::BasicBlock* targetBlock, CleanupCursor cleanupScope, - Statement* targetStatement); + JumpTarget() = default; + JumpTarget(llvm::BasicBlock *targetBlock, CleanupCursor cleanupScope, + Statement *targetStatement); }; /// Keeps track of source and target label of a goto. @@ -65,22 +64,22 @@ struct JumpTarget { /// Used if we cannot immediately emit all the code for a jump because we have /// not generated code for the target yet. struct GotoJump { - // The location of the goto instruction, for error reporting. - Loc sourceLoc; + // The location of the goto instruction, for error reporting. + Loc sourceLoc; - /// The basic block which contains the goto as its terminator. - llvm::BasicBlock* sourceBlock = nullptr; + /// The basic block which contains the goto as its terminator. + llvm::BasicBlock *sourceBlock = nullptr; - /// While we have not found the actual branch target, we might need to - /// create a "fake" basic block in order to be able to execute the cleanups - /// (we do not keep branching information around after leaving the scope). - llvm::BasicBlock* tentativeTarget = nullptr; + /// While we have not found the actual branch target, we might need to + /// create a "fake" basic block in order to be able to execute the cleanups + /// (we do not keep branching information around after leaving the scope). + llvm::BasicBlock *tentativeTarget = nullptr; - /// The label to target with the goto. - Identifier* targetLabel = nullptr; + /// The label to target with the goto. + Identifier *targetLabel = nullptr; - GotoJump(Loc loc, llvm::BasicBlock* sourceBlock, - llvm::BasicBlock* tentativeTarget, Identifier* targetLabel); + GotoJump(Loc loc, llvm::BasicBlock *sourceBlock, + llvm::BasicBlock *tentativeTarget, Identifier *targetLabel); }; /// Describes a particular way to leave a cleanup scope and continue execution @@ -89,16 +88,16 @@ struct GotoJump { /// In general, there can be multiple ones (normal exit, early returns, /// breaks/continues, exceptions, and so on). struct CleanupExitTarget { - explicit CleanupExitTarget(llvm::BasicBlock* t) : branchTarget(t) {} + explicit CleanupExitTarget(llvm::BasicBlock *t) : branchTarget(t) {} - /// The target basic block to branch to after running the cleanup. - llvm::BasicBlock* branchTarget = nullptr; + /// The target basic block to branch to after running the cleanup. + llvm::BasicBlock *branchTarget = nullptr; - /// The basic blocks that want to continue with this target after running - /// the cleanup. We need to keep this information around so we can insert - /// stores to the branch selector variable when converting from one to two - /// targets. - std::vector sourceBlocks; + /// The basic blocks that want to continue with this target after running + /// the cleanup. We need to keep this information around so we can insert + /// stores to the branch selector variable when converting from one to two + /// targets. + std::vector sourceBlocks; }; /// Represents a scope (in abstract terms, not curly braces) that requires a @@ -115,40 +114,40 @@ struct CleanupExitTarget { /// local variables with destructors, each of which might throw itself). class CleanupScope { public: - CleanupScope(llvm::BasicBlock* beginBlock, llvm::BasicBlock* endBlock) : - beginBlock(beginBlock), endBlock(endBlock) {} + CleanupScope(llvm::BasicBlock *beginBlock, llvm::BasicBlock *endBlock) + : beginBlock(beginBlock), endBlock(endBlock) {} - /// The basic block to branch to for running the cleanup. - llvm::BasicBlock* beginBlock = nullptr; + /// The basic block to branch to for running the cleanup. + llvm::BasicBlock *beginBlock = nullptr; - /// The basic block that contains the end of the cleanup code (is different - /// from beginBlock if the cleanup contains control flow). - llvm::BasicBlock* endBlock = nullptr; + /// The basic block that contains the end of the cleanup code (is different + /// from beginBlock if the cleanup contains control flow). + llvm::BasicBlock *endBlock = nullptr; - /// The branch selector variable, or null if not created yet. - llvm::AllocaInst* branchSelector = nullptr; + /// The branch selector variable, or null if not created yet. + llvm::AllocaInst *branchSelector = nullptr; - /// Stores all possible targets blocks after running this cleanup, along - /// with what predecessors want to continue at that target. The index in - /// the vector corresponds to the branch selector value for that target. - // Note: This is of course a bad choice of data structure for many targets - // complexity-wise. However, situations where this matters should be - // exceedingly rare in both hand-written as well as generated code. - std::vector exitTargets; + /// Stores all possible targets blocks after running this cleanup, along + /// with what predecessors want to continue at that target. The index in + /// the vector corresponds to the branch selector value for that target. + // Note: This is of course a bad choice of data structure for many targets + // complexity-wise. However, situations where this matters should be + // exceedingly rare in both hand-written as well as generated code. + std::vector exitTargets; - /// Keeps track of all the gotos originating from somewhere inside this - /// scope for which we have not found the label yet (because it occurs - /// lexically later in the function). - // Note: Should also be a dense map from source block to the rest of the - // data if we expect many gotos. - std::vector unresolvedGotos; + /// Keeps track of all the gotos originating from somewhere inside this + /// scope for which we have not found the label yet (because it occurs + /// lexically later in the function). + // Note: Should also be a dense map from source block to the rest of the + // data if we expect many gotos. + std::vector unresolvedGotos; - /// Caches landing pads generated for catches at this cleanup scope level. - /// - /// One element is pushed to the back on each time a catch block is entered, - /// and popped again once it is left. If the corresponding landing pad has - /// not been generated yet (this is done lazily), the pointer is null. - std::vector landingPads; + /// Caches landing pads generated for catches at this cleanup scope level. + /// + /// One element is pushed to the back on each time a catch block is entered, + /// and popped again once it is left. If the corresponding landing pad has + /// not been generated yet (this is done lazily), the pointer is null. + std::vector landingPads; }; /// Stores information to be able to branch to a catch clause if it matches. @@ -156,18 +155,18 @@ public: /// Each catch body is emitted only once, but may be target from many landing /// pads (in case of nested catch or cleanup scopes). struct CatchScope { - /// The ClassInfo reference corresponding to the type to match the - /// exception object against. - llvm::Constant* classInfoPtr = nullptr; + /// The ClassInfo reference corresponding to the type to match the + /// exception object against. + llvm::Constant *classInfoPtr = nullptr; - /// The block to branch to if the exception type matches. - llvm::BasicBlock* bodyBlock = nullptr; + /// The block to branch to if the exception type matches. + llvm::BasicBlock *bodyBlock = nullptr; - /// The cleanup scope stack level corresponding to this catch. - CleanupCursor cleanupScope; + /// The cleanup scope stack level corresponding to this catch. + CleanupCursor cleanupScope; - CatchScope(llvm::Constant* classInfoPtr, llvm::BasicBlock* bodyBlock, - CleanupCursor cleanupScope); + CatchScope(llvm::Constant *classInfoPtr, llvm::BasicBlock *bodyBlock, + CleanupCursor cleanupScope); }; /// Keeps track of active (abstract) scopes in a function that influence code @@ -188,284 +187,285 @@ struct CatchScope { /// resolving forward references across cleanup scopes. class ScopeStack { public: - explicit ScopeStack(IRState* irs) : irs(irs) {} - ~ScopeStack(); + explicit ScopeStack(IRState *irs) : irs(irs) {} + ~ScopeStack(); - /// Registers a piece of cleanup code to be run. - /// - /// The end block is expected not to contain a terminator yet. It will be - /// added by ScopeStack as needed, based on what follow-up blocks code from - /// within this scope will branch to. - void pushCleanup(llvm::BasicBlock* beginBlock, llvm::BasicBlock* endBlock); + /// Registers a piece of cleanup code to be run. + /// + /// The end block is expected not to contain a terminator yet. It will be + /// added by ScopeStack as needed, based on what follow-up blocks code from + /// within this scope will branch to. + void pushCleanup(llvm::BasicBlock *beginBlock, llvm::BasicBlock *endBlock); - /// Terminates the current basic block with a branch to the cleanups needed - /// for leaving the current scope and continuing execution at the target - /// scope stack level. - /// - /// After running them, execution will branch to the given basic block. - void runCleanups(CleanupCursor targetScope, llvm::BasicBlock* continueWith) { - runCleanups(currentCleanupScope(), targetScope, continueWith); - } + /// Terminates the current basic block with a branch to the cleanups needed + /// for leaving the current scope and continuing execution at the target + /// scope stack level. + /// + /// After running them, execution will branch to the given basic block. + void runCleanups(CleanupCursor targetScope, llvm::BasicBlock *continueWith) { + runCleanups(currentCleanupScope(), targetScope, continueWith); + } - /// Like #runCleanups(), but runs all of them until the top-level scope is - /// reached. - void runAllCleanups(llvm::BasicBlock* continueWith); + /// Like #runCleanups(), but runs all of them until the top-level scope is + /// reached. + void runAllCleanups(llvm::BasicBlock *continueWith); - /// Pops all the cleanups between the current scope and the target cursor. - /// - /// This does not insert any cleanup calls, use #runCleanups() beforehand. - void popCleanups(CleanupCursor targetScope); + /// Pops all the cleanups between the current scope and the target cursor. + /// + /// This does not insert any cleanup calls, use #runCleanups() beforehand. + void popCleanups(CleanupCursor targetScope); - /// Returns a cursor that identifies the current cleanup scope, to be later - /// used with #runCleanups() et al. - /// - /// Note that this cursor is only valid as long as the current scope is not - /// popped. - CleanupCursor currentCleanupScope() { return cleanupScopes.size(); } + /// Returns a cursor that identifies the current cleanup scope, to be later + /// used with #runCleanups() et al. + /// + /// Note that this cursor is only valid as long as the current scope is not + /// popped. + CleanupCursor currentCleanupScope() { return cleanupScopes.size(); } - /// Registers a catch block to be taken into consideration when an exception - /// is thrown within the current scope. - /// - /// When a potentially throwing function call is emitted, a landing pad will - /// be emitted to compare the dynamic type info of the exception against the - /// given ClassInfo constant and to branch to the given body block if it - /// matches. The registered catch blocks are maintained on a stack, with the - /// top-most (i.e. last pushed, innermost) taking precedence. - void pushCatch(llvm::Constant* classInfoPtr, llvm::BasicBlock* bodyBlock); + /// Registers a catch block to be taken into consideration when an exception + /// is thrown within the current scope. + /// + /// When a potentially throwing function call is emitted, a landing pad will + /// be emitted to compare the dynamic type info of the exception against the + /// given ClassInfo constant and to branch to the given body block if it + /// matches. The registered catch blocks are maintained on a stack, with the + /// top-most (i.e. last pushed, innermost) taking precedence. + void pushCatch(llvm::Constant *classInfoPtr, llvm::BasicBlock *bodyBlock); - /// Unregisters the last registered catch block. - void popCatch(); + /// Unregisters the last registered catch block. + void popCatch(); - /// Registers a loop statement to be used as a target for break/continue - /// statements in the current scope. - void pushLoopTarget(Statement* loopStatement, llvm::BasicBlock* continueTarget, - llvm::BasicBlock* breakTarget); + /// Registers a loop statement to be used as a target for break/continue + /// statements in the current scope. + void pushLoopTarget(Statement *loopStatement, + llvm::BasicBlock *continueTarget, + llvm::BasicBlock *breakTarget); - /// Pops the last pushed loop target, so it is no longer taken into - /// consideration for resolving breaks/continues. - void popLoopTarget(); + /// Pops the last pushed loop target, so it is no longer taken into + /// consideration for resolving breaks/continues. + void popLoopTarget(); - /// Registers a statement to be used as a target for break statements in the - /// current scope (currently applies only to switch statements). - void pushBreakTarget(Statement* switchStatement, llvm::BasicBlock* targetBlock); + /// Registers a statement to be used as a target for break statements in the + /// current scope (currently applies only to switch statements). + void pushBreakTarget(Statement *switchStatement, + llvm::BasicBlock *targetBlock); - /// Unregisters the last registered break target. - void popBreakTarget(); + /// Unregisters the last registered break target. + void popBreakTarget(); - /// Adds a label to serve as a target for goto statements. - /// - /// Also causes in-flight forward references to this label to be resolved. - void addLabelTarget(Identifier* labelName, llvm::BasicBlock* targetBlock); + /// Adds a label to serve as a target for goto statements. + /// + /// Also causes in-flight forward references to this label to be resolved. + void addLabelTarget(Identifier *labelName, llvm::BasicBlock *targetBlock); - /// Emits a call or invoke to the given callee, depending on whether there - /// are catches/cleanups active or not. - template - llvm::CallSite callOrInvoke(llvm::Value* callee, const T &args, - const char* name = ""); + /// Emits a call or invoke to the given callee, depending on whether there + /// are catches/cleanups active or not. + template + llvm::CallSite callOrInvoke(llvm::Value *callee, const T &args, + const char *name = ""); - /// Terminates the current basic block with an unconditional branch to the - /// given label, along with the cleanups to execute on the way there. - /// - /// Legal forward references (i.e. within the same function, and not into - /// a cleanup scope) will be resolved. - void jumpToLabel(Loc loc, Identifier* labelName); + /// Terminates the current basic block with an unconditional branch to the + /// given label, along with the cleanups to execute on the way there. + /// + /// Legal forward references (i.e. within the same function, and not into + /// a cleanup scope) will be resolved. + void jumpToLabel(Loc loc, Identifier *labelName); - /// Terminates the current basic block with an unconditional branch to the - /// continue target generated by the given loop statement, along with - /// the cleanups to execute on the way there. - void continueWithLoop(Statement* loopStatement) { - jumpToStatement(continueTargets, loopStatement); - } + /// Terminates the current basic block with an unconditional branch to the + /// continue target generated by the given loop statement, along with + /// the cleanups to execute on the way there. + void continueWithLoop(Statement *loopStatement) { + jumpToStatement(continueTargets, loopStatement); + } - /// Terminates the current basic block with an unconditional branch to the - /// closest loop continue target, along with the cleanups to execute on - /// the way there. - void continueWithClosest() { - jumpToClosest(continueTargets); - } + /// Terminates the current basic block with an unconditional branch to the + /// closest loop continue target, along with the cleanups to execute on + /// the way there. + void continueWithClosest() { jumpToClosest(continueTargets); } - /// Terminates the current basic block with an unconditional branch to the - /// break target generated by the given loop or switch statement, along with - /// the cleanups to execute on the way there. - void breakToStatement(Statement* loopOrSwitchStatement) { - jumpToStatement(breakTargets, loopOrSwitchStatement); - } + /// Terminates the current basic block with an unconditional branch to the + /// break target generated by the given loop or switch statement, along with + /// the cleanups to execute on the way there. + void breakToStatement(Statement *loopOrSwitchStatement) { + jumpToStatement(breakTargets, loopOrSwitchStatement); + } - /// Terminates the current basic block with an unconditional branch to the - /// closest break statement target, along with the cleanups to execute on - /// the way there. - void breakToClosest() { - jumpToClosest(breakTargets); - } + /// Terminates the current basic block with an unconditional branch to the + /// closest break statement target, along with the cleanups to execute on + /// the way there. + void breakToClosest() { jumpToClosest(breakTargets); } private: - /// Internal version that allows specifying the scope at which to start - /// emitting the cleanups. - void runCleanups(CleanupCursor sourceScope, CleanupCursor targetScope, - llvm::BasicBlock* continueWith); + /// Internal version that allows specifying the scope at which to start + /// emitting the cleanups. + void runCleanups(CleanupCursor sourceScope, CleanupCursor targetScope, + llvm::BasicBlock *continueWith); - std::vector& currentUnresolvedGotos(); + std::vector ¤tUnresolvedGotos(); - std::vector& currentLandingPads(); + std::vector ¤tLandingPads(); - /// Emits a landing pad to honor all the active cleanups and catches. - llvm::BasicBlock* emitLandingPad(); + /// Emits a landing pad to honor all the active cleanups and catches. + llvm::BasicBlock *emitLandingPad(); - /// Unified implementation for labeled break/continue. - void jumpToStatement(std::vector& targets, Statement* loopOrSwitchStatement); + /// Unified implementation for labeled break/continue. + void jumpToStatement(std::vector &targets, + Statement *loopOrSwitchStatement); - /// Unified implementation for unlabeled break/continue. - void jumpToClosest(std::vector& targets); + /// Unified implementation for unlabeled break/continue. + void jumpToClosest(std::vector &targets); - /// The ambient IRState. For legacy reasons, there is currently a cyclic - /// dependency between the two. - IRState* irs = nullptr; + /// The ambient IRState. For legacy reasons, there is currently a cyclic + /// dependency between the two. + IRState *irs = nullptr; - using LabelTargetMap = llvm::DenseMap; - /// The labels we have encountered in this function so far, accessed by - /// their associated identifier (i.e. the name of the label). - LabelTargetMap labelTargets; + using LabelTargetMap = llvm::DenseMap; + /// The labels we have encountered in this function so far, accessed by + /// their associated identifier (i.e. the name of the label). + LabelTargetMap labelTargets; - /// - std::vector breakTargets; + /// + std::vector breakTargets; - /// - std::vector continueTargets; + /// + std::vector continueTargets; - /// cleanupScopes[i] contains the information to go from - /// currentCleanupScope() == i + 1 to currentCleanupScope() == i. - std::vector cleanupScopes; + /// cleanupScopes[i] contains the information to go from + /// currentCleanupScope() == i + 1 to currentCleanupScope() == i. + std::vector cleanupScopes; - /// - std::vector catchScopes; + /// + std::vector catchScopes; - /// Gotos which we were not able to resolve to any cleanup scope, but which - /// might still be defined later in the function at top level. If there are - /// any left on function exit, it is an error (e.g. because the user tried - /// to goto into a finally block, etc.). - std::vector topLevelUnresolvedGotos; + /// Gotos which we were not able to resolve to any cleanup scope, but which + /// might still be defined later in the function at top level. If there are + /// any left on function exit, it is an error (e.g. because the user tried + /// to goto into a finally block, etc.). + std::vector topLevelUnresolvedGotos; - /// Caches landing pads generated for catches without any cleanups to run - /// (null if not yet emitted, one element is pushed to/popped from the back - /// on entering/leaving a catch block). - std::vector topLevelLandingPads; + /// Caches landing pads generated for catches without any cleanups to run + /// (null if not yet emitted, one element is pushed to/popped from the back + /// on entering/leaving a catch block). + std::vector topLevelLandingPads; }; template -llvm::CallSite ScopeStack::callOrInvoke(llvm::Value* callee, const T &args, - const char* name -) { - // If this is a direct call, we might be able to use the callee attributes - // to our advantage. - llvm::Function* calleeFn = llvm::dyn_cast(callee); +llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, + const char *name) { + // If this is a direct call, we might be able to use the callee attributes + // to our advantage. + llvm::Function *calleeFn = llvm::dyn_cast(callee); - // Intrinsics don't support invoking and 'nounwind' functions don't need it. - const bool doesNotThrow = calleeFn && - (calleeFn->isIntrinsic() || calleeFn->doesNotThrow()); - - if (doesNotThrow || (cleanupScopes.empty() && catchScopes.empty())) { - llvm::CallInst* call = irs->ir->CreateCall(callee, args, name); - if (calleeFn) { - call->setAttributes(calleeFn->getAttributes()); - } - return call; - } - - if (currentLandingPads().empty()) { - // Have not encountered any catches (for which we would push a scope) or - // calls to throwing functions (where we would have already executed - // this if) in this cleanup scope yet. - currentLandingPads().push_back(0); - } - - llvm::BasicBlock*& landingPad = currentLandingPads().back(); - if (!landingPad) { - landingPad = emitLandingPad(); - } - - llvm::BasicBlock* postinvoke = llvm::BasicBlock::Create(irs->context(), - "postinvoke", irs->topfunc(), landingPad); - llvm::InvokeInst* invoke = irs->ir->CreateInvoke(callee, postinvoke, - landingPad, args, name); + // Intrinsics don't support invoking and 'nounwind' functions don't need it. + const bool doesNotThrow = + calleeFn && (calleeFn->isIntrinsic() || calleeFn->doesNotThrow()); + if (doesNotThrow || (cleanupScopes.empty() && catchScopes.empty())) { + llvm::CallInst *call = irs->ir->CreateCall(callee, args, name); if (calleeFn) { - invoke->setAttributes(calleeFn->getAttributes()); + call->setAttributes(calleeFn->getAttributes()); } + return call; + } - irs->scope() = IRScope(postinvoke); - return invoke; + if (currentLandingPads().empty()) { + // Have not encountered any catches (for which we would push a scope) or + // calls to throwing functions (where we would have already executed + // this if) in this cleanup scope yet. + currentLandingPads().push_back(0); + } + + llvm::BasicBlock *&landingPad = currentLandingPads().back(); + if (!landingPad) { + landingPad = emitLandingPad(); + } + + llvm::BasicBlock *postinvoke = llvm::BasicBlock::Create( + irs->context(), "postinvoke", irs->topfunc(), landingPad); + llvm::InvokeInst *invoke = + irs->ir->CreateInvoke(callee, postinvoke, landingPad, args, name); + + if (calleeFn) { + invoke->setAttributes(calleeFn->getAttributes()); + } + + irs->scope() = IRScope(postinvoke); + return invoke; } // represents a function struct IrFunction { - // constructor - IrFunction(FuncDeclaration* fd); + // constructor + IrFunction(FuncDeclaration *fd); - // annotations - void setNeverInline(); - void setAlwaysInline(); + // annotations + void setNeverInline(); + void setAlwaysInline(); - llvm::AllocaInst* getOrCreateEhPtrSlot(); + llvm::AllocaInst *getOrCreateEhPtrSlot(); - llvm::Function* func = nullptr; - llvm::Instruction* allocapoint = nullptr; - FuncDeclaration* decl = nullptr; - TypeFunction* type = nullptr; + llvm::Function *func = nullptr; + llvm::Instruction *allocapoint = nullptr; + FuncDeclaration *decl = nullptr; + TypeFunction *type = nullptr; - /// Points to the associated scope stack while emitting code for the function. - ScopeStack* scopes = nullptr; + /// Points to the associated scope stack while emitting code for the function. + ScopeStack *scopes = nullptr; - llvm::Value* retArg = nullptr; // return in ptr arg - llvm::Value* thisArg = nullptr; // class/struct 'this' arg - llvm::Value* nestArg = nullptr; // nested function 'this' arg + llvm::Value *retArg = nullptr; // return in ptr arg + llvm::Value *thisArg = nullptr; // class/struct 'this' arg + llvm::Value *nestArg = nullptr; // nested function 'this' arg - llvm::Value* nestedVar = nullptr; // alloca for the nested context of this function - llvm::StructType* frameType = nullptr; // type of nested context - unsigned frameTypeAlignment = 0; // its alignment - // number of enclosing functions with variables accessed by nested functions - // (-1 if neither this function nor any enclosing ones access variables from enclosing functions) - int depth = -1; - bool nestedContextCreated = false; // holds whether nested context is created + llvm::Value *nestedVar = + nullptr; // alloca for the nested context of this function + llvm::StructType *frameType = nullptr; // type of nested context + unsigned frameTypeAlignment = 0; // its alignment + // number of enclosing functions with variables accessed by nested functions + // (-1 if neither this function nor any enclosing ones access variables from + // enclosing functions) + int depth = -1; + bool nestedContextCreated = false; // holds whether nested context is created - llvm::Value* _arguments = nullptr; - llvm::Value* _argptr = nullptr; + llvm::Value *_arguments = nullptr; + llvm::Value *_argptr = nullptr; - /// A stack slot containing the return value, for functions that return by value. - llvm::AllocaInst* retValSlot = nullptr; - /// The basic block with the return instruction. - llvm::BasicBlock* retBlock = nullptr; + /// A stack slot containing the return value, for functions that return by + /// value. + llvm::AllocaInst *retValSlot = nullptr; + /// The basic block with the return instruction. + llvm::BasicBlock *retBlock = nullptr; - /// A stack slot containing the exception object pointer while a landing pad - /// is active. Need this because the instruction must dominate all uses as a - /// _d_eh_resume_unwind parameter, but if we take a select at the end on a - /// cleanup on the way there, it also must dominate all other predecessors - /// of the cleanup. Thus, we just create an alloca at the start of the - /// function. - llvm::AllocaInst* ehPtrSlot = nullptr; - /// The basic block with the return instruction. Because of - /// ehPtrSlot, we do not need more than one, so might as well - /// cache it. - llvm::BasicBlock* resumeUnwindBlock = nullptr; + /// A stack slot containing the exception object pointer while a landing pad + /// is active. Need this because the instruction must dominate all uses as a + /// _d_eh_resume_unwind parameter, but if we take a select at the end on a + /// cleanup on the way there, it also must dominate all other predecessors + /// of the cleanup. Thus, we just create an alloca at the start of the + /// function. + llvm::AllocaInst *ehPtrSlot = nullptr; + /// The basic block with the return instruction. Because of + /// ehPtrSlot, we do not need more than one, so might as well + /// cache it. + llvm::BasicBlock *resumeUnwindBlock = nullptr; - /// Similar story to ehPtrSlot, but for the selector value. - llvm::AllocaInst* ehSelectorSlot = nullptr; + /// Similar story to ehPtrSlot, but for the selector value. + llvm::AllocaInst *ehSelectorSlot = nullptr; #if LDC_LLVM_VER >= 307 - llvm::DISubprogram* diSubprogram = nullptr; - std::stack diLexicalBlocks; - using VariableMap = llvm::DenseMap; + llvm::DISubprogram *diSubprogram = nullptr; + std::stack diLexicalBlocks; + using VariableMap = llvm::DenseMap; #else - llvm::DISubprogram diSubprogram; - std::stack diLexicalBlocks; - using VariableMap = llvm::DenseMap; + llvm::DISubprogram diSubprogram; + std::stack diLexicalBlocks; + using VariableMap = llvm::DenseMap; #endif - // Debug info for all variables - VariableMap variableMap; + // Debug info for all variables + VariableMap variableMap; - IrFuncTy irFty; + IrFuncTy irFty; }; -IrFunction* getIrFunc(FuncDeclaration* decl, bool create = false); -bool isIrFuncCreated(FuncDeclaration* decl); +IrFunction *getIrFunc(FuncDeclaration *decl, bool create = false); +bool isIrFuncCreated(FuncDeclaration *decl); #endif diff --git a/ir/irfuncty.cpp b/ir/irfuncty.cpp index 99445bce3d..2e11f7198f 100644 --- a/ir/irfuncty.cpp +++ b/ir/irfuncty.cpp @@ -15,85 +15,78 @@ #include "gen/logger.h" #include "gen/tollvm.h" -IrFuncTyArg::IrFuncTyArg(Type* t, bool bref, const AttrBuilder& a) +IrFuncTyArg::IrFuncTyArg(Type *t, bool bref, const AttrBuilder &a) : type(t), ltype(t != Type::tvoid && bref ? DtoType(t->pointerTo()) : DtoType(t)), - attrs(a), - byref(bref) -{} + attrs(a), byref(bref) {} bool IrFuncTyArg::isInReg() const { return attrs.contains(LLAttribute::InReg); } -bool IrFuncTyArg::isSRet() const { return attrs.contains(LLAttribute::StructRet); } +bool IrFuncTyArg::isSRet() const { + return attrs.contains(LLAttribute::StructRet); +} bool IrFuncTyArg::isByVal() const { return attrs.contains(LLAttribute::ByVal); } -llvm::Value* IrFuncTy::putRet(DValue* dval) -{ - assert(!arg_sret); +llvm::Value *IrFuncTy::putRet(DValue *dval) { + assert(!arg_sret); - if (ret->rewrite) { - Logger::println("Rewrite: putRet"); - LOG_SCOPE - return ret->rewrite->put(dval); - } + if (ret->rewrite) { + Logger::println("Rewrite: putRet"); + LOG_SCOPE + return ret->rewrite->put(dval); + } - return dval->getRVal(); + return dval->getRVal(); } -llvm::Value* IrFuncTy::getRet(Type* dty, LLValue* val) -{ - assert(!arg_sret); +llvm::Value *IrFuncTy::getRet(Type *dty, LLValue *val) { + assert(!arg_sret); - if (ret->rewrite) { - Logger::println("Rewrite: getRet"); - LOG_SCOPE - return ret->rewrite->get(dty, val); - } + if (ret->rewrite) { + Logger::println("Rewrite: getRet"); + LOG_SCOPE + return ret->rewrite->get(dty, val); + } - return val; + return val; } -void IrFuncTy::getRet(Type* dty, LLValue* val, LLValue* address) -{ - assert(!arg_sret); +void IrFuncTy::getRet(Type *dty, LLValue *val, LLValue *address) { + assert(!arg_sret); - if (ret->rewrite) { - Logger::println("Rewrite: getRet (getL)"); - LOG_SCOPE - ret->rewrite->getL(dty, val, address); - return; - } + if (ret->rewrite) { + Logger::println("Rewrite: getRet (getL)"); + LOG_SCOPE + ret->rewrite->getL(dty, val, address); + return; + } - DtoStoreZextI8(val, address); + DtoStoreZextI8(val, address); } -llvm::Value* IrFuncTy::putParam(size_t idx, DValue* dval) -{ - assert(idx < args.size() && "invalid putParam"); - return putParam(*args[idx], dval); +llvm::Value *IrFuncTy::putParam(size_t idx, DValue *dval) { + assert(idx < args.size() && "invalid putParam"); + return putParam(*args[idx], dval); } -llvm::Value* IrFuncTy::putParam(const IrFuncTyArg& arg, DValue* dval) -{ - if (arg.rewrite) { - Logger::println("Rewrite: putParam"); - LOG_SCOPE - return arg.rewrite->put(dval); - } +llvm::Value *IrFuncTy::putParam(const IrFuncTyArg &arg, DValue *dval) { + if (arg.rewrite) { + Logger::println("Rewrite: putParam"); + LOG_SCOPE + return arg.rewrite->put(dval); + } - return dval->getRVal(); + return dval->getRVal(); } -void IrFuncTy::getParam(Type* dty, size_t idx, LLValue* val, LLValue* address) -{ - assert(idx < args.size() && "invalid getParam"); +void IrFuncTy::getParam(Type *dty, size_t idx, LLValue *val, LLValue *address) { + assert(idx < args.size() && "invalid getParam"); - if (args[idx]->rewrite) - { - Logger::println("Rewrite: getParam (getL)"); - LOG_SCOPE - args[idx]->rewrite->getL(dty, val, address); - return; - } + if (args[idx]->rewrite) { + Logger::println("Rewrite: getParam (getL)"); + LOG_SCOPE + args[idx]->rewrite->getL(dty, val, address); + return; + } - DtoStoreZextI8(val, address); + DtoStoreZextI8(val, address); } diff --git a/ir/irfuncty.h b/ir/irfuncty.h index 5dc0daf41c..5dcd577b4b 100644 --- a/ir/irfuncty.h +++ b/ir/irfuncty.h @@ -12,7 +12,6 @@ // //===----------------------------------------------------------------------===// - #ifndef LDC_IR_IRFUNCTY_H #define LDC_IR_IRFUNCTY_H @@ -30,11 +29,11 @@ class DValue; class Type; struct ABIRewrite; namespace llvm { - class Type; - class Value; - class Instruction; - class Function; - class FunctionType; +class Type; +class Value; +class Instruction; +class Function; +class FunctionType; } /// Represents a function type argument (both explicit and implicit as well as @@ -42,83 +41,81 @@ namespace llvm { /// /// Instances of this only exist for arguments that are actually lowered to an /// LLVM parameter (e.g. not for empty structs). -struct IrFuncTyArg -{ - /** This is the original D type as the frontend knows it - * May NOT be rewritten!!! */ - Type* const type = nullptr; +struct IrFuncTyArg { + /** This is the original D type as the frontend knows it + * May NOT be rewritten!!! */ + Type *const type = nullptr; - /// The index of the declaration in the FuncDeclaration::parameters array - /// corresponding to this argument. - size_t parametersIdx = 0; + /// The index of the declaration in the FuncDeclaration::parameters array + /// corresponding to this argument. + size_t parametersIdx = 0; - /// This is the final LLVM Type used for the parameter/return value type - llvm::Type* ltype = nullptr; + /// This is the final LLVM Type used for the parameter/return value type + llvm::Type *ltype = nullptr; - /** These are the final LLVM attributes used for the function. - * Must be valid for the LLVM Type and byref setting */ - AttrBuilder attrs; + /** These are the final LLVM attributes used for the function. + * Must be valid for the LLVM Type and byref setting */ + AttrBuilder attrs; - /** 'true' if the final LLVM argument is a LLVM reference type. - * Must be true when the D Type is a value type, but the final - * LLVM Type is a reference type! */ - bool byref = false; + /** 'true' if the final LLVM argument is a LLVM reference type. + * Must be true when the D Type is a value type, but the final + * LLVM Type is a reference type! */ + bool byref = false; - /** Pointer to the ABIRewrite structure needed to rewrite LLVM ValueS - * to match the final LLVM Type when passing arguments and getting - * return values */ - ABIRewrite* rewrite = nullptr; + /** Pointer to the ABIRewrite structure needed to rewrite LLVM ValueS + * to match the final LLVM Type when passing arguments and getting + * return values */ + ABIRewrite *rewrite = nullptr; - /// Helper to check if the 'inreg' attribute is set - bool isInReg() const; - /// Helper to check if the 'sret' attribute is set - bool isSRet() const; - /// Helper to check if the 'byval' attribute is set - bool isByVal() const; + /// Helper to check if the 'inreg' attribute is set + bool isInReg() const; + /// Helper to check if the 'sret' attribute is set + bool isSRet() const; + /// Helper to check if the 'byval' attribute is set + bool isByVal() const; - /** @param t D type of argument/return value as known by the frontend - * @param byref Initial value for the 'byref' field. If true the initial - * LLVM Type will be of DtoType(type->pointerTo()), instead - * of just DtoType(type) */ - IrFuncTyArg(Type* t, bool byref, const AttrBuilder& attrs = AttrBuilder()); + /** @param t D type of argument/return value as known by the frontend + * @param byref Initial value for the 'byref' field. If true the initial + * LLVM Type will be of DtoType(type->pointerTo()), instead + * of just DtoType(type) */ + IrFuncTyArg(Type *t, bool byref, const AttrBuilder &attrs = AttrBuilder()); }; // represents a function type -struct IrFuncTy -{ - // The final LLVM type - llvm::FunctionType* funcType = nullptr; +struct IrFuncTy { + // The final LLVM type + llvm::FunctionType *funcType = nullptr; - // return value - IrFuncTyArg* ret = nullptr; + // return value + IrFuncTyArg *ret = nullptr; - // null if not applicable - IrFuncTyArg* arg_sret = nullptr; - IrFuncTyArg* arg_this = nullptr; - IrFuncTyArg* arg_nest = nullptr; - IrFuncTyArg* arg_arguments = nullptr; + // null if not applicable + IrFuncTyArg *arg_sret = nullptr; + IrFuncTyArg *arg_this = nullptr; + IrFuncTyArg *arg_nest = nullptr; + IrFuncTyArg *arg_arguments = nullptr; - // normal explicit arguments -// typedef llvm::SmallVector ArgList; - using ArgList = std::vector; - ArgList args; + // normal explicit arguments + // typedef llvm::SmallVector ArgList; + using ArgList = std::vector; + ArgList args; - // C varargs - bool c_vararg = false; + // C varargs + bool c_vararg = false; - // range of normal parameters to reverse - bool reverseParams = false; + // range of normal parameters to reverse + bool reverseParams = false; - // reserved for ABI-specific data - void* tag = nullptr; + // reserved for ABI-specific data + void *tag = nullptr; - llvm::Value* putRet(DValue* dval); - llvm::Value* getRet(Type* dty, llvm::Value* val); - void getRet(Type* dty, llvm::Value* val, llvm::Value* address); + llvm::Value *putRet(DValue *dval); + llvm::Value *getRet(Type *dty, llvm::Value *val); + void getRet(Type *dty, llvm::Value *val, llvm::Value *address); - llvm::Value* putParam(size_t idx, DValue* dval); - llvm::Value* putParam(const IrFuncTyArg& arg, DValue* dval); - void getParam(Type* dty, size_t idx, llvm::Value* val, llvm::Value* address); + llvm::Value *putParam(size_t idx, DValue *dval); + llvm::Value *putParam(const IrFuncTyArg &arg, DValue *dval); + void getParam(Type *dty, size_t idx, llvm::Value *val, llvm::Value *address); }; #endif diff --git a/ir/irmodule.cpp b/ir/irmodule.cpp index 943379374a..5b3879a040 100644 --- a/ir/irmodule.cpp +++ b/ir/irmodule.cpp @@ -15,32 +15,32 @@ #include "ir/irdsymbol.h" #include "ir/irfunction.h" -IrModule::IrModule(Module* module, const char* srcfilename) - : M(module) -{} +IrModule::IrModule(Module *module, const char *srcfilename) : M(module) {} -llvm::GlobalVariable* IrModule::moduleInfoSymbol() { - if (moduleInfoVar) return moduleInfoVar; - - std::string name("_D"); - name.append(mangle(M)); - name.append("12__ModuleInfoZ"); - - moduleInfoVar = new llvm::GlobalVariable( - gIR->module, llvm::StructType::create(gIR->context()), false, - llvm::GlobalValue::ExternalLinkage, NULL, name); +llvm::GlobalVariable *IrModule::moduleInfoSymbol() { + if (moduleInfoVar) return moduleInfoVar; + + std::string name("_D"); + name.append(mangle(M)); + name.append("12__ModuleInfoZ"); + + moduleInfoVar = new llvm::GlobalVariable( + gIR->module, llvm::StructType::create(gIR->context()), false, + llvm::GlobalValue::ExternalLinkage, NULL, name); + return moduleInfoVar; } -IrModule* getIrModule(Module* m) { - if (!m) m = gIR->func()->decl->getModule(); +IrModule *getIrModule(Module *m) { + if (!m) + m = gIR->func()->decl->getModule(); - assert(m && "null module"); - if (m->ir.m_type == IrDsymbol::NotSet) { - m->ir.irModule = new IrModule(m, m->srcfile->toChars()); - m->ir.m_type = IrDsymbol::ModuleType; - } + assert(m && "null module"); + if (m->ir.m_type == IrDsymbol::NotSet) { + m->ir.irModule = new IrModule(m, m->srcfile->toChars()); + m->ir.m_type = IrDsymbol::ModuleType; + } - assert(m->ir.m_type == IrDsymbol::ModuleType); - return m->ir.irModule; + assert(m->ir.m_type == IrDsymbol::ModuleType); + return m->ir.irModule; } diff --git a/ir/irmodule.h b/ir/irmodule.h index eead465fe4..d325d34d09 100644 --- a/ir/irmodule.h +++ b/ir/irmodule.h @@ -24,28 +24,28 @@ class GlobalVariable; } struct IrModule { - IrModule(Module* module, const char* srcfilename); - virtual ~IrModule() = default; + IrModule(Module *module, const char *srcfilename); + virtual ~IrModule() = default; - Module* const M = nullptr; + Module *const M = nullptr; - llvm::GlobalVariable* moduleInfoSymbol(); + llvm::GlobalVariable *moduleInfoSymbol(); - // static ctors/dtors/unittests - using FuncDeclList = std::list; - using GatesList = std::list; - FuncDeclList ctors; - FuncDeclList dtors; - FuncDeclList sharedCtors; - FuncDeclList sharedDtors; - GatesList gates; - GatesList sharedGates; - FuncDeclList unitTests; + // static ctors/dtors/unittests + using FuncDeclList = std::list; + using GatesList = std::list; + FuncDeclList ctors; + FuncDeclList dtors; + FuncDeclList sharedCtors; + FuncDeclList sharedDtors; + GatesList gates; + GatesList sharedGates; + FuncDeclList unitTests; private: - llvm::GlobalVariable* moduleInfoVar = nullptr; + llvm::GlobalVariable *moduleInfoVar = nullptr; }; -IrModule* getIrModule(Module* m); +IrModule *getIrModule(Module *m); #endif diff --git a/ir/irtype.cpp b/ir/irtype.cpp index 6d1914a690..e44fb820fd 100644 --- a/ir/irtype.cpp +++ b/ir/irtype.cpp @@ -16,253 +16,220 @@ #include "gen/tollvm.h" #include "ir/irtype.h" -// This code uses llvm::getGlobalContext() as these functions are invoked before gIR is set. +// This code uses llvm::getGlobalContext() as these functions are invoked before +// gIR is set. // ... thus it segfaults on gIR==NULL ////////////////////////////////////////////////////////////////////////////// -IrType::IrType(Type* dt, LLType* lt) -: dtype(dt), - type(lt) -{ - assert(dt && "null D Type"); - assert(lt && "null LLVM Type"); - assert(!dt->ctype && "already has IrType"); +IrType::IrType(Type *dt, LLType *lt) : dtype(dt), type(lt) { + assert(dt && "null D Type"); + assert(lt && "null LLVM Type"); + assert(!dt->ctype && "already has IrType"); } ////////////////////////////////////////////////////////////////////////////// -IrFuncTy &IrType::getIrFuncTy() -{ - llvm_unreachable("cannot get IrFuncTy from non lazy/function/delegate"); +IrFuncTy &IrType::getIrFuncTy() { + llvm_unreachable("cannot get IrFuncTy from non lazy/function/delegate"); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -IrTypeBasic::IrTypeBasic(Type * dt) -: IrType(dt, basic2llvm(dt)) -{ +IrTypeBasic::IrTypeBasic(Type *dt) : IrType(dt, basic2llvm(dt)) {} + +////////////////////////////////////////////////////////////////////////////// + +IrTypeBasic *IrTypeBasic::get(Type *dt) { + IrTypeBasic *t = new IrTypeBasic(dt); + dt->ctype = t; + return t; } ////////////////////////////////////////////////////////////////////////////// -IrTypeBasic* IrTypeBasic::get(Type* dt) -{ - IrTypeBasic* t = new IrTypeBasic(dt); - dt->ctype = t; - return t; +LLType *IrTypeBasic::getComplexType(llvm::LLVMContext &ctx, LLType *type) { + llvm::Type *types[] = {type, type}; + return llvm::StructType::get(ctx, types, false); } ////////////////////////////////////////////////////////////////////////////// -LLType* IrTypeBasic::getComplexType(llvm::LLVMContext& ctx, LLType* type) -{ - llvm::Type *types[] = { type, type }; - return llvm::StructType::get(ctx, types, false); +static inline llvm::Type *getReal80Type(llvm::LLVMContext &ctx) { + llvm::Triple::ArchType const a = global.params.targetTriple.getArch(); + bool const anyX86 = (a == llvm::Triple::x86) || (a == llvm::Triple::x86_64); + + // only x86 has 80bit float - but no support with MS C Runtime! + if (anyX86 && !global.params.targetTriple.isWindowsMSVCEnvironment()) + return llvm::Type::getX86_FP80Ty(ctx); + + return llvm::Type::getDoubleTy(ctx); } ////////////////////////////////////////////////////////////////////////////// -static inline llvm::Type* getReal80Type(llvm::LLVMContext& ctx) -{ - llvm::Triple::ArchType const a = global.params.targetTriple.getArch(); - bool const anyX86 = (a == llvm::Triple::x86) || (a == llvm::Triple::x86_64); +llvm::Type *IrTypeBasic::basic2llvm(Type *t) { + llvm::LLVMContext &ctx = llvm::getGlobalContext(); - // only x86 has 80bit float - but no support with MS C Runtime! - if (anyX86 && !global.params.targetTriple.isWindowsMSVCEnvironment()) - return llvm::Type::getX86_FP80Ty(ctx); + switch (t->ty) { + case Tvoid: + return llvm::Type::getVoidTy(ctx); + case Tint8: + case Tuns8: + case Tchar: + return llvm::Type::getInt8Ty(ctx); + + case Tint16: + case Tuns16: + case Twchar: + return llvm::Type::getInt16Ty(ctx); + + case Tint32: + case Tuns32: + case Tdchar: + return llvm::Type::getInt32Ty(ctx); + + case Tint64: + case Tuns64: + return llvm::Type::getInt64Ty(ctx); + + case Tint128: + case Tuns128: + return llvm::IntegerType::get(ctx, 128); + + case Tfloat32: + case Timaginary32: + return llvm::Type::getFloatTy(ctx); + + case Tfloat64: + case Timaginary64: return llvm::Type::getDoubleTy(ctx); -} -////////////////////////////////////////////////////////////////////////////// + case Tfloat80: + case Timaginary80: + return getReal80Type(ctx); -llvm::Type * IrTypeBasic::basic2llvm(Type* t) -{ - llvm::LLVMContext& ctx = llvm::getGlobalContext(); + case Tcomplex32: + return getComplexType(ctx, llvm::Type::getFloatTy(ctx)); - switch(t->ty) - { - case Tvoid: - return llvm::Type::getVoidTy(ctx); + case Tcomplex64: + return getComplexType(ctx, llvm::Type::getDoubleTy(ctx)); - case Tint8: - case Tuns8: - case Tchar: - return llvm::Type::getInt8Ty(ctx); + case Tcomplex80: + return getComplexType(ctx, getReal80Type(ctx)); - case Tint16: - case Tuns16: - case Twchar: - return llvm::Type::getInt16Ty(ctx); - - case Tint32: - case Tuns32: - case Tdchar: - return llvm::Type::getInt32Ty(ctx); - - case Tint64: - case Tuns64: - return llvm::Type::getInt64Ty(ctx); - - case Tint128: - case Tuns128: - return llvm::IntegerType::get(ctx, 128); - - case Tfloat32: - case Timaginary32: - return llvm::Type::getFloatTy(ctx); - - case Tfloat64: - case Timaginary64: - return llvm::Type::getDoubleTy(ctx); - - case Tfloat80: - case Timaginary80: - return getReal80Type(ctx); - - case Tcomplex32: - return getComplexType(ctx, llvm::Type::getFloatTy(ctx)); - - case Tcomplex64: - return getComplexType(ctx, llvm::Type::getDoubleTy(ctx)); - - case Tcomplex80: - return getComplexType(ctx, getReal80Type(ctx)); - - case Tbool: - return llvm::Type::getInt1Ty(ctx); - default: - llvm_unreachable("Unknown basic type."); - } + case Tbool: + return llvm::Type::getInt1Ty(ctx); + default: + llvm_unreachable("Unknown basic type."); + } } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -IrTypePointer::IrTypePointer(Type* dt, LLType* lt) -: IrType(dt, lt) -{ -} +IrTypePointer::IrTypePointer(Type *dt, LLType *lt) : IrType(dt, lt) {} ////////////////////////////////////////////////////////////////////////////// -IrTypePointer* IrTypePointer::get(Type* dt) -{ - assert(!dt->ctype); - assert((dt->ty == Tpointer || dt->ty == Tnull) && "not pointer/null type"); +IrTypePointer *IrTypePointer::get(Type *dt) { + assert(!dt->ctype); + assert((dt->ty == Tpointer || dt->ty == Tnull) && "not pointer/null type"); - LLType* elemType; - if (dt->ty == Tnull) - { - elemType = llvm::Type::getInt8Ty(llvm::getGlobalContext()); - } - else - { - elemType = DtoMemType(dt->nextOf()); + LLType *elemType; + if (dt->ty == Tnull) { + elemType = llvm::Type::getInt8Ty(llvm::getGlobalContext()); + } else { + elemType = DtoMemType(dt->nextOf()); - // DtoType could have already created the same type, e.g. for - // dt == Node* in struct Node { Node* n; }. - if (dt->ctype) - return dt->ctype->isPointer(); - } + // DtoType could have already created the same type, e.g. for + // dt == Node* in struct Node { Node* n; }. + if (dt->ctype) + return dt->ctype->isPointer(); + } - IrTypePointer* t = new IrTypePointer(dt, llvm::PointerType::get(elemType, 0)); - dt->ctype = t; - return t; + IrTypePointer *t = new IrTypePointer(dt, llvm::PointerType::get(elemType, 0)); + dt->ctype = t; + return t; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -IrTypeSArray::IrTypeSArray(Type * dt) -: IrType(dt, sarray2llvm(dt)) -{ +IrTypeSArray::IrTypeSArray(Type *dt) : IrType(dt, sarray2llvm(dt)) {} + +////////////////////////////////////////////////////////////////////////////// + +IrTypeSArray *IrTypeSArray::get(Type *dt) { + IrTypeSArray *t = new IrTypeSArray(dt); + dt->ctype = t; + return t; } ////////////////////////////////////////////////////////////////////////////// -IrTypeSArray* IrTypeSArray::get(Type* dt) -{ - IrTypeSArray* t = new IrTypeSArray(dt); - dt->ctype = t; - return t; -} - -////////////////////////////////////////////////////////////////////////////// - -llvm::Type * IrTypeSArray::sarray2llvm(Type * t) -{ - assert(t->ty == Tsarray && "not static array type"); - TypeSArray* tsa = static_cast(t); - uint64_t dim = static_cast(tsa->dim->toUInteger()); - LLType* elemType = DtoMemType(t->nextOf()); - return llvm::ArrayType::get(elemType, dim); +llvm::Type *IrTypeSArray::sarray2llvm(Type *t) { + assert(t->ty == Tsarray && "not static array type"); + TypeSArray *tsa = static_cast(t); + uint64_t dim = static_cast(tsa->dim->toUInteger()); + LLType *elemType = DtoMemType(t->nextOf()); + return llvm::ArrayType::get(elemType, dim); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -IrTypeArray::IrTypeArray(Type* dt, LLType* lt) -: IrType(dt, lt) -{ -} +IrTypeArray::IrTypeArray(Type *dt, LLType *lt) : IrType(dt, lt) {} ////////////////////////////////////////////////////////////////////////////// -IrTypeArray* IrTypeArray::get(Type* dt) -{ - assert(!dt->ctype); - assert(dt->ty == Tarray && "not dynamic array type"); +IrTypeArray *IrTypeArray::get(Type *dt) { + assert(!dt->ctype); + assert(dt->ty == Tarray && "not dynamic array type"); - LLType* elemType = DtoMemType(dt->nextOf()); + LLType *elemType = DtoMemType(dt->nextOf()); - // Could have already built the type as part of a struct forward reference, - // just as for pointers. - if (!dt->ctype) - { - llvm::Type *types[] = { DtoSize_t(), llvm::PointerType::get(elemType, 0) }; - LLType* at = llvm::StructType::get(llvm::getGlobalContext(), types, false); - dt->ctype = new IrTypeArray(dt, at); - } + // Could have already built the type as part of a struct forward reference, + // just as for pointers. + if (!dt->ctype) { + llvm::Type *types[] = {DtoSize_t(), llvm::PointerType::get(elemType, 0)}; + LLType *at = llvm::StructType::get(llvm::getGlobalContext(), types, false); + dt->ctype = new IrTypeArray(dt, at); + } - return dt->ctype->isArray(); + return dt->ctype->isArray(); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -IrTypeVector::IrTypeVector(Type* dt) -: IrType(dt, vector2llvm(dt)) -{ +IrTypeVector::IrTypeVector(Type *dt) : IrType(dt, vector2llvm(dt)) {} + +////////////////////////////////////////////////////////////////////////////// + +IrTypeVector *IrTypeVector::get(Type *dt) { + IrTypeVector *t = new IrTypeVector(dt); + dt->ctype = t; + return t; } ////////////////////////////////////////////////////////////////////////////// -IrTypeVector* IrTypeVector::get(Type* dt) -{ - IrTypeVector* t = new IrTypeVector(dt); - dt->ctype = t; - return t; -} - -////////////////////////////////////////////////////////////////////////////// - -llvm::Type* IrTypeVector::vector2llvm(Type* dt) -{ - assert(dt->ty == Tvector && "not vector type"); - TypeVector* tv = static_cast(dt); - assert(tv->basetype->ty == Tsarray); - TypeSArray* tsa = static_cast(tv->basetype); - uint64_t dim = static_cast(tsa->dim->toUInteger()); - LLType* elemType = DtoMemType(tsa->next); - return llvm::VectorType::get(elemType, dim); +llvm::Type *IrTypeVector::vector2llvm(Type *dt) { + assert(dt->ty == Tvector && "not vector type"); + TypeVector *tv = static_cast(dt); + assert(tv->basetype->ty == Tsarray); + TypeSArray *tsa = static_cast(tv->basetype); + uint64_t dim = static_cast(tsa->dim->toUInteger()); + LLType *elemType = DtoMemType(tsa->next); + return llvm::VectorType::get(elemType, dim); } ////////////////////////////////////////////////////////////////////////////// diff --git a/ir/irtype.h b/ir/irtype.h index 9242a90f6f..d81fa730dd 100644 --- a/ir/irtype.h +++ b/ir/irtype.h @@ -12,7 +12,6 @@ // //===----------------------------------------------------------------------===// - #ifndef __LDC_IR_IRTYPE_H__ #define __LDC_IR_IRTYPE_H__ @@ -22,10 +21,9 @@ // forward declarations -namespace llvm -{ - class LLVMContext; - class Type; +namespace llvm { +class LLVMContext; +class Type; } class Type; @@ -59,143 +57,137 @@ class IrTypeVector; /// TODO: Implement the described changes (now that the forward reference /// handling logic seems to work correctly) and get rid of the "no-op" DtoType /// calls in IrAggr, ... that only exist for their side effect. -class IrType -{ +class IrType { public: - virtual ~IrType() = default; + virtual ~IrType() = default; - /// - virtual IrTypeAggr* isAggr() { return nullptr; } - /// - virtual IrTypeArray* isArray() { return nullptr; } - /// - virtual IrTypeBasic* isBasic() { return nullptr; } - /// - virtual IrTypeClass* isClass() { return nullptr; } - /// - virtual IrTypeDelegate* isDelegate(){ return nullptr; } - /// - virtual IrTypeFunction* isFunction(){ return nullptr; } - /// - virtual IrTypePointer* isPointer() { return nullptr; } - /// - virtual IrTypeSArray* isSArray() { return nullptr; } - /// - virtual IrTypeStruct* isStruct() { return nullptr; } - /// - virtual IrTypeVector* isVector() { return nullptr; } + /// + virtual IrTypeAggr *isAggr() { return nullptr; } + /// + virtual IrTypeArray *isArray() { return nullptr; } + /// + virtual IrTypeBasic *isBasic() { return nullptr; } + /// + virtual IrTypeClass *isClass() { return nullptr; } + /// + virtual IrTypeDelegate *isDelegate() { return nullptr; } + /// + virtual IrTypeFunction *isFunction() { return nullptr; } + /// + virtual IrTypePointer *isPointer() { return nullptr; } + /// + virtual IrTypeSArray *isSArray() { return nullptr; } + /// + virtual IrTypeStruct *isStruct() { return nullptr; } + /// + virtual IrTypeVector *isVector() { return nullptr; } - /// - Type* getDType() { return dtype; } - /// - virtual llvm::Type* getLLType() { return type; } + /// + Type *getDType() { return dtype; } + /// + virtual llvm::Type *getLLType() { return type; } - /// - virtual IrFuncTy& getIrFuncTy(); + /// + virtual IrFuncTy &getIrFuncTy(); protected: - /// - IrType(Type* dt, llvm::Type* lt); + /// + IrType(Type *dt, llvm::Type *lt); - /// - Type* dtype = nullptr; + /// + Type *dtype = nullptr; - /// LLVM type. - llvm::Type* type = nullptr; + /// LLVM type. + llvm::Type *type = nullptr; }; ////////////////////////////////////////////////////////////////////////////// /// IrType for basic D types. -class IrTypeBasic : public IrType -{ +class IrTypeBasic : public IrType { public: - /// - static IrTypeBasic* get(Type* dt); + /// + static IrTypeBasic *get(Type *dt); - /// - IrTypeBasic* isBasic() { return this; } + /// + IrTypeBasic *isBasic() { return this; } protected: - /// - IrTypeBasic(Type* dt); - /// - static llvm::Type* getComplexType(llvm::LLVMContext& ctx, llvm::Type* type); - /// - static llvm::Type* basic2llvm(Type* t); + /// + IrTypeBasic(Type *dt); + /// + static llvm::Type *getComplexType(llvm::LLVMContext &ctx, llvm::Type *type); + /// + static llvm::Type *basic2llvm(Type *t); }; ////////////////////////////////////////////////////////////////////////////// /// IrType from pointers. -class IrTypePointer : public IrType -{ +class IrTypePointer : public IrType { public: - /// - static IrTypePointer* get(Type* dt); + /// + static IrTypePointer *get(Type *dt); - /// - IrTypePointer* isPointer() { return this; } + /// + IrTypePointer *isPointer() { return this; } protected: - /// - IrTypePointer(Type* dt, llvm::Type *lt); + /// + IrTypePointer(Type *dt, llvm::Type *lt); }; ////////////////////////////////////////////////////////////////////////////// /// IrType for static arrays -class IrTypeSArray : public IrType -{ +class IrTypeSArray : public IrType { public: - /// - static IrTypeSArray* get(Type* dt); + /// + static IrTypeSArray *get(Type *dt); - /// - IrTypeSArray* isSArray() { return this; } + /// + IrTypeSArray *isSArray() { return this; } protected: - /// - IrTypeSArray(Type* dt); + /// + IrTypeSArray(Type *dt); - /// - static llvm::Type* sarray2llvm(Type* t); + /// + static llvm::Type *sarray2llvm(Type *t); }; ////////////////////////////////////////////////////////////////////////////// /// IrType for dynamic arrays -class IrTypeArray : public IrType -{ +class IrTypeArray : public IrType { public: - /// - static IrTypeArray* get(Type* dt); + /// + static IrTypeArray *get(Type *dt); - /// - IrTypeArray* isArray() { return this; } + /// + IrTypeArray *isArray() { return this; } protected: - /// - IrTypeArray(Type* dt, llvm::Type *lt); + /// + IrTypeArray(Type *dt, llvm::Type *lt); }; ////////////////////////////////////////////////////////////////////////////// /// IrType for vectors -class IrTypeVector : public IrType -{ +class IrTypeVector : public IrType { public: - /// - static IrTypeVector* get(Type* dt); + /// + static IrTypeVector *get(Type *dt); - /// - IrTypeVector* isVector() { return this; } + /// + IrTypeVector *isVector() { return this; } protected: - /// - IrTypeVector(Type* dt); + /// + IrTypeVector(Type *dt); - static llvm::Type* vector2llvm(Type* dt); + static llvm::Type *vector2llvm(Type *dt); }; #endif diff --git a/ir/irtypeaggr.cpp b/ir/irtypeaggr.cpp index 13363beead..4f412431f4 100644 --- a/ir/irtypeaggr.cpp +++ b/ir/irtypeaggr.cpp @@ -22,227 +22,204 @@ ////////////////////////////////////////////////////////////////////////////// // FIXME A similar function is in ir/iraggr.cpp and RTTIBuilder::push(). -static inline -size_t add_zeros(std::vector& defaultTypes, - size_t startOffset, size_t endOffset) -{ - assert(startOffset <= endOffset); - const size_t paddingSize = endOffset - startOffset; - if (paddingSize) - { - llvm::ArrayType* pad = llvm::ArrayType::get(llvm::Type::getInt8Ty(gIR->context()), paddingSize); - defaultTypes.push_back(pad); - } - return paddingSize ? 1 : 0; +static inline size_t add_zeros(std::vector &defaultTypes, + size_t startOffset, size_t endOffset) { + assert(startOffset <= endOffset); + const size_t paddingSize = endOffset - startOffset; + if (paddingSize) { + llvm::ArrayType *pad = llvm::ArrayType::get( + llvm::Type::getInt8Ty(gIR->context()), paddingSize); + defaultTypes.push_back(pad); + } + return paddingSize ? 1 : 0; } - -bool var_offset_sort_cb(const VarDeclaration* v1, const VarDeclaration* v2) -{ - if (v1 && v2) - return v1->offset < v2->offset; - // sort NULL pointers towards the end - return v1 && !v2; +bool var_offset_sort_cb(const VarDeclaration *v1, const VarDeclaration *v2) { + if (v1 && v2) + return v1->offset < v2->offset; + // sort NULL pointers towards the end + return v1 && !v2; } -AggrTypeBuilder::AggrTypeBuilder(bool packed) : - m_packed(packed) -{ - m_defaultTypes.reserve(32); +AggrTypeBuilder::AggrTypeBuilder(bool packed) : m_packed(packed) { + m_defaultTypes.reserve(32); } -void AggrTypeBuilder::addType(llvm::Type *type, unsigned size) -{ - m_defaultTypes.push_back(type); - m_offset += size; - m_fieldIndex++; +void AggrTypeBuilder::addType(llvm::Type *type, unsigned size) { + m_defaultTypes.push_back(type); + m_offset += size; + m_fieldIndex++; } -void AggrTypeBuilder::addAggregate(AggregateDeclaration *ad) -{ - // mirror the ad->fields array but only fill in contributors - const size_t n = ad->fields.dim; - LLSmallVector data(n, NULL); +void AggrTypeBuilder::addAggregate(AggregateDeclaration *ad) { + // mirror the ad->fields array but only fill in contributors + const size_t n = ad->fields.dim; + LLSmallVector data(n, NULL); - unsigned int errors = global.errors; + unsigned int errors = global.errors; - // first fill in the fields with explicit initializers - for (size_t index = 0; index < n; ++index) - { - VarDeclaration *field = ad->fields[index]; + // first fill in the fields with explicit initializers + for (size_t index = 0; index < n; ++index) { + VarDeclaration *field = ad->fields[index]; - // init is !null for explicit inits - if (field->init != NULL && !field->init->isVoidInitializer()) - { - IF_LOG Logger::println("adding explicit initializer for struct field %s", - field->toChars()); + // init is !null for explicit inits + if (field->init != NULL && !field->init->isVoidInitializer()) { + IF_LOG Logger::println("adding explicit initializer for struct field %s", + field->toChars()); - size_t f_size = field->type->size(); - size_t f_begin = field->offset; - size_t f_end = f_begin + f_size; - if (f_size == 0) - continue; + size_t f_size = field->type->size(); + size_t f_begin = field->offset; + size_t f_end = f_begin + f_size; + if (f_size == 0) + continue; - data[index] = field; + data[index] = field; - // make sure there is no overlap - for (size_t i = 0; i < index; i++) - { - if (data[i] != NULL) - { - VarDeclaration* vd = data[i]; - size_t v_begin = vd->offset; - size_t v_end = v_begin + vd->type->size(); + // make sure there is no overlap + for (size_t i = 0; i < index; i++) { + if (data[i] != NULL) { + VarDeclaration *vd = data[i]; + size_t v_begin = vd->offset; + size_t v_end = v_begin + vd->type->size(); - if (v_begin >= f_end || v_end <= f_begin) - continue; - - ad->error(vd->loc, "has overlapping initialization for %s and %s", - field->toChars(), vd->toChars()); - } - } - } - } - - if (errors != global.errors) - { - // There was an overlapping initialization. - // Return if errors are gagged otherwise abort. - if (global.gag) return; - fatal(); - } - - // fill in default initializers - for (size_t index = 0; index < n; ++index) - { - if (data[index]) - continue; - VarDeclaration *field = ad->fields[index]; - - size_t f_size = field->type->size(); - size_t f_begin = field->offset; - size_t f_end = f_begin + f_size; - if (f_size == 0) + if (v_begin >= f_end || v_end <= f_begin) continue; - // make sure it doesn't overlap anything explicit - bool overlaps = false; - for (size_t i = 0; i < n; i++) - { - if (data[i]) - { - size_t v_begin = data[i]->offset; - size_t v_end = v_begin + data[i]->type->size(); - - if (v_begin >= f_end || v_end <= f_begin) - continue; - - overlaps = true; - break; - } + ad->error(vd->loc, "has overlapping initialization for %s and %s", + field->toChars(), vd->toChars()); } + } + } + } - // if no overlap was found, add the default initializer - if (!overlaps) - { - IF_LOG Logger::println("adding default initializer for struct field %s", - field->toChars()); - data[index] = field; - } + if (errors != global.errors) { + // There was an overlapping initialization. + // Return if errors are gagged otherwise abort. + if (global.gag) + return; + fatal(); + } + + // fill in default initializers + for (size_t index = 0; index < n; ++index) { + if (data[index]) + continue; + VarDeclaration *field = ad->fields[index]; + + size_t f_size = field->type->size(); + size_t f_begin = field->offset; + size_t f_end = f_begin + f_size; + if (f_size == 0) + continue; + + // make sure it doesn't overlap anything explicit + bool overlaps = false; + for (size_t i = 0; i < n; i++) { + if (data[i]) { + size_t v_begin = data[i]->offset; + size_t v_end = v_begin + data[i]->type->size(); + + if (v_begin >= f_end || v_end <= f_begin) + continue; + + overlaps = true; + break; + } } - // - // ok. now we can build a list of llvm types. and make sure zeros are inserted if necessary. - // - - // first we sort the list by offset - std::sort(data.begin(), data.end(), var_offset_sort_cb); - - // add types to list - for (size_t i = 0; i < n; i++) - { - VarDeclaration* vd = data[i]; - - if (vd == NULL) - continue; - - assert(vd->offset >= m_offset && "Variable overlaps previous field."); - - // Add an explicit field for any padding so we can zero it, as per TDPL §7.1.1. - if (m_offset < vd->offset) { - m_fieldIndex += add_zeros(m_defaultTypes, m_offset, vd->offset); - m_offset = vd->offset; - } - - // add default type - m_defaultTypes.push_back(DtoMemType(vd->type)); - - // advance offset to right past this field - m_offset += getMemberSize(vd->type); - - // set the field index - m_varGEPIndices[vd] = m_fieldIndex; - ++m_fieldIndex; + // if no overlap was found, add the default initializer + if (!overlaps) { + IF_LOG Logger::println("adding default initializer for struct field %s", + field->toChars()); + data[index] = field; } + } + + // + // ok. now we can build a list of llvm types. and make sure zeros are inserted + // if necessary. + // + + // first we sort the list by offset + std::sort(data.begin(), data.end(), var_offset_sort_cb); + + // add types to list + for (size_t i = 0; i < n; i++) { + VarDeclaration *vd = data[i]; + + if (vd == NULL) + continue; + + assert(vd->offset >= m_offset && "Variable overlaps previous field."); + + // Add an explicit field for any padding so we can zero it, as per TDPL + // §7.1.1. + if (m_offset < vd->offset) { + m_fieldIndex += add_zeros(m_defaultTypes, m_offset, vd->offset); + m_offset = vd->offset; + } + + // add default type + m_defaultTypes.push_back(DtoMemType(vd->type)); + + // advance offset to right past this field + m_offset += getMemberSize(vd->type); + + // set the field index + m_varGEPIndices[vd] = m_fieldIndex; + ++m_fieldIndex; + } } -void AggrTypeBuilder::alignCurrentOffset(unsigned alignment) -{ - m_overallAlignment = std::max(alignment, m_overallAlignment); +void AggrTypeBuilder::alignCurrentOffset(unsigned alignment) { + m_overallAlignment = std::max(alignment, m_overallAlignment); - unsigned aligned = (m_offset + alignment - 1) & ~(alignment - 1); - if (m_offset < aligned) { - m_fieldIndex += add_zeros(m_defaultTypes, m_offset, aligned); - m_offset = aligned; - } + unsigned aligned = (m_offset + alignment - 1) & ~(alignment - 1); + if (m_offset < aligned) { + m_fieldIndex += add_zeros(m_defaultTypes, m_offset, aligned); + m_offset = aligned; + } } -void AggrTypeBuilder::addTailPadding(unsigned aggregateSize) -{ - // tail padding? - if (m_offset < aggregateSize) - { - add_zeros(m_defaultTypes, m_offset, aggregateSize); - } +void AggrTypeBuilder::addTailPadding(unsigned aggregateSize) { + // tail padding? + if (m_offset < aggregateSize) { + add_zeros(m_defaultTypes, m_offset, aggregateSize); + } } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -IrTypeAggr::IrTypeAggr(AggregateDeclaration * ad) -: IrType(ad->type, LLStructType::create(gIR->context(), ad->toPrettyChars())), - aggr(ad) -{} +IrTypeAggr::IrTypeAggr(AggregateDeclaration *ad) + : IrType(ad->type, + LLStructType::create(gIR->context(), ad->toPrettyChars())), + aggr(ad) {} -bool IrTypeAggr::isPacked(AggregateDeclaration* ad) -{ - if (ad->isUnionDeclaration()) return true; - for (unsigned i = 0; i < ad->fields.dim; i++) - { - VarDeclaration* vd = static_cast(ad->fields.data[i]); - unsigned a = vd->type->alignsize() - 1; - if (((vd->offset + a) & ~a) != vd->offset) - return true; - } - return false; +bool IrTypeAggr::isPacked(AggregateDeclaration *ad) { + if (ad->isUnionDeclaration()) + return true; + for (unsigned i = 0; i < ad->fields.dim; i++) { + VarDeclaration *vd = static_cast(ad->fields.data[i]); + unsigned a = vd->type->alignsize() - 1; + if (((vd->offset + a) & ~a) != vd->offset) + return true; + } + return false; } -void IrTypeAggr::getMemberLocation(VarDeclaration* var, unsigned& fieldIndex, - unsigned& byteOffset) const -{ - // Note: The interface is a bit more general than what we actually return. - // Specifically, the frontend offset information we use for overlapping - // fields is always based at the object start. - std::map::const_iterator it = - varGEPIndices.find(var); - if (it != varGEPIndices.end()) - { - fieldIndex = it->second; - byteOffset = 0; - } - else - { - fieldIndex = 0; - byteOffset = var->offset; - } +void IrTypeAggr::getMemberLocation(VarDeclaration *var, unsigned &fieldIndex, + unsigned &byteOffset) const { + // Note: The interface is a bit more general than what we actually return. + // Specifically, the frontend offset information we use for overlapping + // fields is always based at the object start. + std::map::const_iterator it = + varGEPIndices.find(var); + if (it != varGEPIndices.end()) { + fieldIndex = it->second; + byteOffset = 0; + } else { + fieldIndex = 0; + byteOffset = var->offset; + } } diff --git a/ir/irtypeaggr.h b/ir/irtypeaggr.h index 5178fc0beb..8717dc2d48 100644 --- a/ir/irtypeaggr.h +++ b/ir/irtypeaggr.h @@ -17,80 +17,79 @@ #include namespace llvm { - class Constant; - class StructType; +class Constant; +class StructType; } class AggregateDeclaration; class VarDeclaration; -using VarGEPIndices = std::map; +using VarGEPIndices = std::map; -class AggrTypeBuilder -{ +class AggrTypeBuilder { public: - explicit AggrTypeBuilder(bool packed); - void addType(llvm::Type *type, unsigned size); - void addAggregate(AggregateDeclaration *ad); - void alignCurrentOffset(unsigned alignment); - void addTailPadding(unsigned aggregateSize); - unsigned currentFieldIndex() const { return m_fieldIndex; } - std::vector defaultTypes() const { return m_defaultTypes; } - VarGEPIndices varGEPIndices() const { return m_varGEPIndices; } - unsigned overallAlignment() const { return m_overallAlignment; } + explicit AggrTypeBuilder(bool packed); + void addType(llvm::Type *type, unsigned size); + void addAggregate(AggregateDeclaration *ad); + void alignCurrentOffset(unsigned alignment); + void addTailPadding(unsigned aggregateSize); + unsigned currentFieldIndex() const { return m_fieldIndex; } + std::vector defaultTypes() const { return m_defaultTypes; } + VarGEPIndices varGEPIndices() const { return m_varGEPIndices; } + unsigned overallAlignment() const { return m_overallAlignment; } + protected: - std::vector m_defaultTypes; - VarGEPIndices m_varGEPIndices; - unsigned m_offset = 0; - unsigned m_fieldIndex = 0; - unsigned m_overallAlignment = 0; - bool m_packed = false; + std::vector m_defaultTypes; + VarGEPIndices m_varGEPIndices; + unsigned m_offset = 0; + unsigned m_fieldIndex = 0; + unsigned m_overallAlignment = 0; + bool m_packed = false; }; /// Base class of IrTypes for aggregate types. -class IrTypeAggr : public IrType -{ +class IrTypeAggr : public IrType { public: - /// - IrTypeAggr* isAggr() { return this; } + /// + IrTypeAggr *isAggr() { return this; } - /// Returns the index of the field in the LLVM struct type that corresponds - /// to the given member variable, plus the offset to the actual field start - /// due to overlapping (union) fields, if any. - void getMemberLocation(VarDeclaration* var, unsigned& fieldIndex, - unsigned& byteOffset) const; + /// Returns the index of the field in the LLVM struct type that corresponds + /// to the given member variable, plus the offset to the actual field start + /// due to overlapping (union) fields, if any. + void getMemberLocation(VarDeclaration *var, unsigned &fieldIndex, + unsigned &byteOffset) const; - /// Composite type debug description. This is not only to cache, but also - /// used for resolving forward references. +/// Composite type debug description. This is not only to cache, but also +/// used for resolving forward references. #if LDC_LLVM_VER >= 307 - llvm::DIType* diCompositeType = nullptr; + llvm::DIType *diCompositeType = nullptr; #else - llvm::DIType diCompositeType; + llvm::DIType diCompositeType; #endif - /// true, if the LLVM struct type for the aggregate is declared as packed - bool packed = false; + /// true, if the LLVM struct type for the aggregate is declared as packed + bool packed = false; protected: - /// - IrTypeAggr(AggregateDeclaration* ad); + /// + IrTypeAggr(AggregateDeclaration *ad); - /// Returns true, if the LLVM struct type for the aggregate must be declared - /// as packed. - static bool isPacked(AggregateDeclaration* ad); + /// Returns true, if the LLVM struct type for the aggregate must be declared + /// as packed. + static bool isPacked(AggregateDeclaration *ad); - /// AggregateDeclaration this type represents. - AggregateDeclaration* aggr = nullptr; + /// AggregateDeclaration this type represents. + AggregateDeclaration *aggr = nullptr; - /// Stores the mapping from member variables to field indices in the actual - /// LLVM type. If a member variable is not present, this means that it does - /// not resolve to a "clean" GEP but extra offsetting due to overlapping - /// members is needed (i.e., a union). - /// - /// We need to keep track of this separately, because there is no way to get - /// the field index of a variable in the frontend, it only stores the byte - /// offset. - VarGEPIndices varGEPIndices; + /// Stores the mapping from member variables to field indices in the actual + /// LLVM type. If a member variable is not present, this means that it does + /// not resolve to a "clean" GEP but extra offsetting due to overlapping + /// members is needed (i.e., a union). + /// + /// We need to keep track of this separately, because there is no way to get + /// the field index of a variable in the frontend, it only stores the byte + /// offset. + VarGEPIndices varGEPIndices; }; #endif diff --git a/ir/irtypeclass.cpp b/ir/irtypeclass.cpp index 7bc8e0d82f..46b128ff5f 100644 --- a/ir/irtypeclass.cpp +++ b/ir/irtypeclass.cpp @@ -25,229 +25,211 @@ ////////////////////////////////////////////////////////////////////////////// -IrTypeClass::IrTypeClass(ClassDeclaration* cd) -: IrTypeAggr(cd), - cd(cd), - tc(static_cast(cd->type)) -{ - std::string vtbl_name(cd->toPrettyChars()); - vtbl_name.append(".__vtbl"); - vtbl_type = LLStructType::create(gIR->context(), vtbl_name); - vtbl_size = cd->vtbl.dim; +IrTypeClass::IrTypeClass(ClassDeclaration *cd) + : IrTypeAggr(cd), cd(cd), tc(static_cast(cd->type)) { + std::string vtbl_name(cd->toPrettyChars()); + vtbl_name.append(".__vtbl"); + vtbl_type = LLStructType::create(gIR->context(), vtbl_name); + vtbl_size = cd->vtbl.dim; } ////////////////////////////////////////////////////////////////////////////// -void IrTypeClass::addBaseClassData(AggrTypeBuilder &builder, ClassDeclaration *base) -{ - if (base->baseClass) - { - addBaseClassData(builder, base->baseClass); - } - - builder.addAggregate(base); - - // any interface implementations? - if (base->vtblInterfaces && base->vtblInterfaces->dim > 0) - { - bool new_instances = (base == cd); - - VarDeclaration *interfaces_idx = Type::typeinfoclass->fields[3]; - Type* first = interfaces_idx->type->nextOf()->pointerTo(); - - // align offset - builder.alignCurrentOffset(Target::ptrsize); - - for (auto b : *base->vtblInterfaces) - { - IF_LOG Logger::println("Adding interface vtbl for %s", b->base->toPrettyChars()); - - FuncDeclarations arr; - b->fillVtbl(cd, &arr, new_instances); - - // add to the interface map - addInterfaceToMap(b->base, builder.currentFieldIndex()); - - llvm::Type* ivtbl_type = llvm::StructType::get(gIR->context(), buildVtblType(first, &arr)); - builder.addType(llvm::PointerType::get(ivtbl_type, 0), Target::ptrsize); - - // inc count - num_interface_vtbls++; - } +void IrTypeClass::addBaseClassData(AggrTypeBuilder &builder, + ClassDeclaration *base) { + if (base->baseClass) { + addBaseClassData(builder, base->baseClass); + } + + builder.addAggregate(base); + + // any interface implementations? + if (base->vtblInterfaces && base->vtblInterfaces->dim > 0) { + bool new_instances = (base == cd); + + VarDeclaration *interfaces_idx = Type::typeinfoclass->fields[3]; + Type *first = interfaces_idx->type->nextOf()->pointerTo(); + + // align offset + builder.alignCurrentOffset(Target::ptrsize); + + for (auto b : *base->vtblInterfaces) { + IF_LOG Logger::println("Adding interface vtbl for %s", + b->base->toPrettyChars()); + + FuncDeclarations arr; + b->fillVtbl(cd, &arr, new_instances); + + // add to the interface map + addInterfaceToMap(b->base, builder.currentFieldIndex()); + + llvm::Type *ivtbl_type = + llvm::StructType::get(gIR->context(), buildVtblType(first, &arr)); + builder.addType(llvm::PointerType::get(ivtbl_type, 0), Target::ptrsize); + + // inc count + num_interface_vtbls++; } + } } ////////////////////////////////////////////////////////////////////////////// -IrTypeClass* IrTypeClass::get(ClassDeclaration* cd) -{ - IrTypeClass* t = new IrTypeClass(cd); - cd->type->ctype = t; +IrTypeClass *IrTypeClass::get(ClassDeclaration *cd) { + IrTypeClass *t = new IrTypeClass(cd); + cd->type->ctype = t; - IF_LOG Logger::println("Building class type %s @ %s", cd->toPrettyChars(), cd->loc.toChars()); - LOG_SCOPE; - IF_LOG Logger::println("Instance size: %u", cd->structsize); + IF_LOG Logger::println("Building class type %s @ %s", cd->toPrettyChars(), + cd->loc.toChars()); + LOG_SCOPE; + IF_LOG Logger::println("Instance size: %u", cd->structsize); - // This class may contain an align declaration. See issue 726. - t->packed = false; - for (ClassDeclaration *base = cd; base != 0 && !t->packed; base = base->baseClass) - { - t->packed = isPacked(base); + // This class may contain an align declaration. See issue 726. + t->packed = false; + for (ClassDeclaration *base = cd; base != 0 && !t->packed; + base = base->baseClass) { + t->packed = isPacked(base); + } + + AggrTypeBuilder builder(t->packed); + + // add vtbl + builder.addType(llvm::PointerType::get(t->vtbl_type, 0), Target::ptrsize); + + // interfaces are just a vtable + if (cd->isInterfaceDeclaration()) { + t->num_interface_vtbls = cd->vtblInterfaces ? cd->vtblInterfaces->dim : 0; + } + // classes have monitor and fields + else { + if (!cd->isCPPclass() && !cd->isCPPinterface()) { + // add monitor + builder.addType( + llvm::PointerType::get(llvm::Type::getInt8Ty(gIR->context()), 0), + Target::ptrsize); } - AggrTypeBuilder builder(t->packed); + // add data members recursively + t->addBaseClassData(builder, cd); - // add vtbl - builder.addType(llvm::PointerType::get(t->vtbl_type, 0), Target::ptrsize); + // add tail padding + builder.addTailPadding(cd->structsize); + } - // interfaces are just a vtable - if (cd->isInterfaceDeclaration()) - { - t->num_interface_vtbls = cd->vtblInterfaces ? cd->vtblInterfaces->dim : 0; - } - // classes have monitor and fields - else - { - if (!cd->isCPPclass() && !cd->isCPPinterface()) - { - // add monitor - builder.addType(llvm::PointerType::get(llvm::Type::getInt8Ty(gIR->context()), 0), Target::ptrsize); - } + // errors are fatal during codegen + if (global.errors) + fatal(); - // add data members recursively - t->addBaseClassData(builder, cd); + // set struct body and copy GEP indices + isaStruct(t->type)->setBody(builder.defaultTypes(), t->packed); + t->varGEPIndices = builder.varGEPIndices(); - // add tail padding - builder.addTailPadding(cd->structsize); - } + // VTBL - // errors are fatal during codegen - if (global.errors) - fatal(); + // set vtbl type body + FuncDeclarations vtbl; + vtbl.reserve(cd->vtbl.dim); + vtbl.push(0); + for (size_t i = cd->vtblOffset(); i < cd->vtbl.dim; ++i) { + FuncDeclaration *fd = cd->vtbl[i]->isFuncDeclaration(); + assert(fd); + vtbl.push(fd); + } + t->vtbl_type->setBody(t->buildVtblType(Type::typeinfoclass->type, &vtbl)); - // set struct body and copy GEP indices - isaStruct(t->type)->setBody(builder.defaultTypes(), t->packed); - t->varGEPIndices = builder.varGEPIndices(); + IF_LOG Logger::cout() << "class type: " << *t->type << std::endl; - // VTBL - - // set vtbl type body - FuncDeclarations vtbl; - vtbl.reserve(cd->vtbl.dim); - vtbl.push(0); - for (size_t i = cd->vtblOffset(); i < cd->vtbl.dim; ++i) - { - FuncDeclaration *fd = cd->vtbl[i]->isFuncDeclaration(); - assert(fd); - vtbl.push(fd); - } - t->vtbl_type->setBody(t->buildVtblType(Type::typeinfoclass->type, &vtbl)); - - IF_LOG Logger::cout() << "class type: " << *t->type << std::endl; - - return t; + return t; } ////////////////////////////////////////////////////////////////////////////// -std::vector IrTypeClass::buildVtblType(Type* first, FuncDeclarations* vtbl_array) -{ - IF_LOG Logger::println("Building vtbl type for class %s", cd->toPrettyChars()); - LOG_SCOPE; +std::vector +IrTypeClass::buildVtblType(Type *first, FuncDeclarations *vtbl_array) { + IF_LOG Logger::println("Building vtbl type for class %s", + cd->toPrettyChars()); + LOG_SCOPE; - std::vector types; - types.reserve(vtbl_array->dim); + std::vector types; + types.reserve(vtbl_array->dim); - // first comes the classinfo - if (!cd->isCPPclass() && !cd->isCPPinterface()) - types.push_back(DtoType(first)); - - // then come the functions - for (auto I = vtbl_array->begin() + 1, E = vtbl_array->end(); I != E; ++I) - { - FuncDeclaration* fd = *I; - if (fd == NULL) - { - // FIXME - // why is this null? - // happens for mini/s.d - types.push_back(getVoidPtrType()); - continue; - } - - IF_LOG Logger::println("Adding type of %s", fd->toPrettyChars()); - - // If inferring return type and semantic3 has not been run, do it now. - // This pops up in some other places in the frontend as well, however - // it is probably a bug that it still occurs that late. - if (!fd->type->nextOf() && fd->inferRetType) - { - Logger::println("Running late semantic3 to infer return type."); - TemplateInstance *spec = fd->isSpeculative(); - unsigned int olderrs = global.errors; - fd->semantic3(fd->scope); - if (spec && global.errors != olderrs) - spec->errors = global.errors - olderrs; - } - - if (!fd->type->nextOf()) { - // Return type of the function has not been inferred. This seems to - // happen with virtual functions and is probably a frontend bug. - IF_LOG Logger::println("Broken function type, semanticRun: %d", - fd->semanticRun); - types.push_back(getVoidPtrType()); - continue; - } - - types.push_back(getPtrToType(DtoFunctionType(fd))); + // first comes the classinfo + if (!cd->isCPPclass() && !cd->isCPPinterface()) + types.push_back(DtoType(first)); + // then come the functions + for (auto I = vtbl_array->begin() + 1, E = vtbl_array->end(); I != E; ++I) { + FuncDeclaration *fd = *I; + if (fd == NULL) { + // FIXME + // why is this null? + // happens for mini/s.d + types.push_back(getVoidPtrType()); + continue; } - return types; -} + IF_LOG Logger::println("Adding type of %s", fd->toPrettyChars()); -////////////////////////////////////////////////////////////////////////////// - -llvm::Type * IrTypeClass::getLLType() -{ - return llvm::PointerType::get(type, 0); -} - -////////////////////////////////////////////////////////////////////////////// - -llvm::Type * IrTypeClass::getMemoryLLType() -{ - return type; -} - -////////////////////////////////////////////////////////////////////////////// - -size_t IrTypeClass::getInterfaceIndex(ClassDeclaration * inter) -{ - auto it = interfaceMap.find(inter); - if (it == interfaceMap.end()) - return ~0UL; - return it->second; -} - -////////////////////////////////////////////////////////////////////////////// - -void IrTypeClass::addInterfaceToMap(ClassDeclaration * inter, size_t index) -{ - // don't duplicate work or overwrite indices - if (interfaceMap.find(inter) != interfaceMap.end()) - return; - - // add this interface - interfaceMap.insert(std::make_pair(inter, index)); - - // add the direct base interfaces recursively - they - // are accessed through the same index - if (inter->interfaces_dim > 0) - { - BaseClass* b = inter->interfaces[0]; - addInterfaceToMap(b->base, index); + // If inferring return type and semantic3 has not been run, do it now. + // This pops up in some other places in the frontend as well, however + // it is probably a bug that it still occurs that late. + if (!fd->type->nextOf() && fd->inferRetType) { + Logger::println("Running late semantic3 to infer return type."); + TemplateInstance *spec = fd->isSpeculative(); + unsigned int olderrs = global.errors; + fd->semantic3(fd->scope); + if (spec && global.errors != olderrs) + spec->errors = global.errors - olderrs; } + + if (!fd->type->nextOf()) { + // Return type of the function has not been inferred. This seems to + // happen with virtual functions and is probably a frontend bug. + IF_LOG Logger::println("Broken function type, semanticRun: %d", + fd->semanticRun); + types.push_back(getVoidPtrType()); + continue; + } + + types.push_back(getPtrToType(DtoFunctionType(fd))); + } + + return types; +} + +////////////////////////////////////////////////////////////////////////////// + +llvm::Type *IrTypeClass::getLLType() { return llvm::PointerType::get(type, 0); } + +////////////////////////////////////////////////////////////////////////////// + +llvm::Type *IrTypeClass::getMemoryLLType() { return type; } + +////////////////////////////////////////////////////////////////////////////// + +size_t IrTypeClass::getInterfaceIndex(ClassDeclaration *inter) { + auto it = interfaceMap.find(inter); + if (it == interfaceMap.end()) + return ~0UL; + return it->second; +} + +////////////////////////////////////////////////////////////////////////////// + +void IrTypeClass::addInterfaceToMap(ClassDeclaration *inter, size_t index) { + // don't duplicate work or overwrite indices + if (interfaceMap.find(inter) != interfaceMap.end()) + return; + + // add this interface + interfaceMap.insert(std::make_pair(inter, index)); + + // add the direct base interfaces recursively - they + // are accessed through the same index + if (inter->interfaces_dim > 0) { + BaseClass *b = inter->interfaces[0]; + addInterfaceToMap(b->base, index); + } } ////////////////////////////////////////////////////////////////////////////// diff --git a/ir/irtypeclass.h b/ir/irtypeclass.h index f37fb5efa0..07f0b756f8 100644 --- a/ir/irtypeclass.h +++ b/ir/irtypeclass.h @@ -18,77 +18,77 @@ #include "llvm/IR/DerivedTypes.h" template struct Array; -using FuncDeclarations = Array; +using FuncDeclarations = Array; /// -class IrTypeClass : public IrTypeAggr -{ +class IrTypeClass : public IrTypeAggr { public: - /// - static IrTypeClass* get(ClassDeclaration* cd); + /// + static IrTypeClass *get(ClassDeclaration *cd); - /// - virtual IrTypeClass* isClass() { return this; } + /// + virtual IrTypeClass *isClass() { return this; } - /// - llvm::Type* getLLType(); + /// + llvm::Type *getLLType(); - /// Returns the actual storage type, i.e. without the indirection - /// for the class reference. - llvm::Type* getMemoryLLType(); + /// Returns the actual storage type, i.e. without the indirection + /// for the class reference. + llvm::Type *getMemoryLLType(); - /// Returns the vtable type for this class. - llvm::Type* getVtbl() { return vtbl_type; } + /// Returns the vtable type for this class. + llvm::Type *getVtbl() { return vtbl_type; } - /// Get index to interface implementation. - /// Returns the index of a specific interface implementation in this - /// class or ~0 if not found. - size_t getInterfaceIndex(ClassDeclaration* inter); + /// Get index to interface implementation. + /// Returns the index of a specific interface implementation in this + /// class or ~0 if not found. + size_t getInterfaceIndex(ClassDeclaration *inter); - /// Returns the total number of pointers in the vtable. - unsigned getVtblSize() { return vtbl_size; } + /// Returns the total number of pointers in the vtable. + unsigned getVtblSize() { return vtbl_size; } - /// Returns the number of interface implementations (vtables) in this - /// class. - unsigned getNumInterfaceVtbls() { return num_interface_vtbls; } + /// Returns the number of interface implementations (vtables) in this + /// class. + unsigned getNumInterfaceVtbls() { return num_interface_vtbls; } protected: - /// - IrTypeClass(ClassDeclaration* cd); + /// + IrTypeClass(ClassDeclaration *cd); - /// - ClassDeclaration* cd = nullptr; - /// - TypeClass* tc = nullptr; + /// + ClassDeclaration *cd = nullptr; + /// + TypeClass *tc = nullptr; - /// Vtable type. - llvm::StructType* vtbl_type = nullptr; + /// Vtable type. + llvm::StructType *vtbl_type = nullptr; - /// Number of pointers in vtable. - unsigned vtbl_size = 0; + /// Number of pointers in vtable. + unsigned vtbl_size = 0; - /// Number of interface implementations (vtables) in this class. - unsigned num_interface_vtbls = 0; + /// Number of interface implementations (vtables) in this class. + unsigned num_interface_vtbls = 0; - /// std::map type mapping ClassDeclaration* to size_t. - using ClassIndexMap = std::map; + /// std::map type mapping ClassDeclaration* to size_t. + using ClassIndexMap = std::map; - /// Map for mapping the index of a specific interface implementation - /// in this class to its ClassDeclaration. - ClassIndexMap interfaceMap; + /// Map for mapping the index of a specific interface implementation + /// in this class to its ClassDeclaration. + ClassIndexMap interfaceMap; - ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// - /// Builds a vtable type given the type of the first entry and an array - /// of all entries. - std::vector buildVtblType(Type* first, FuncDeclarations* vtbl_array); + /// Builds a vtable type given the type of the first entry and an array + /// of all entries. + std::vector buildVtblType(Type *first, + FuncDeclarations *vtbl_array); - /// - void addBaseClassData(AggrTypeBuilder &builder, ClassDeclaration *base); + /// + void addBaseClassData(AggrTypeBuilder &builder, ClassDeclaration *base); - /// Adds the interface and all it's base interface to the interface - /// to index map. - void addInterfaceToMap(ClassDeclaration* inter, size_t index); + /// Adds the interface and all it's base interface to the interface + /// to index map. + void addInterfaceToMap(ClassDeclaration *inter, size_t index); }; #endif diff --git a/ir/irtypefunction.cpp b/ir/irtypefunction.cpp index fb4d6cc597..5a888aae93 100644 --- a/ir/irtypefunction.cpp +++ b/ir/irtypefunction.cpp @@ -16,44 +16,38 @@ #include "ir/irtypefunction.h" -IrTypeFunction::IrTypeFunction(Type* dt, llvm::Type* lt, const IrFuncTy& irFty_) -: IrType(dt, lt), irFty(irFty_) -{ -} +IrTypeFunction::IrTypeFunction(Type *dt, llvm::Type *lt, const IrFuncTy &irFty_) + : IrType(dt, lt), irFty(irFty_) {} -IrTypeFunction* IrTypeFunction::get(Type* dt) -{ - assert(!dt->ctype); - assert(dt->ty == Tfunction); +IrTypeFunction *IrTypeFunction::get(Type *dt) { + assert(!dt->ctype); + assert(dt->ty == Tfunction); - IrFuncTy irFty; - llvm::Type* lt = DtoFunctionType(dt, irFty, NULL, NULL); + IrFuncTy irFty; + llvm::Type *lt = DtoFunctionType(dt, irFty, NULL, NULL); - IrTypeFunction* result = new IrTypeFunction(dt, lt, irFty); - dt->ctype = result; - return result; + IrTypeFunction *result = new IrTypeFunction(dt, lt, irFty); + dt->ctype = result; + return result; } ////////////////////////////////////////////////////////////////////////////// -IrTypeDelegate::IrTypeDelegate(Type* dt, llvm::Type* lt, const IrFuncTy& irFty_) -: IrType(dt, lt), irFty(irFty_) -{ -} - -IrTypeDelegate* IrTypeDelegate::get(Type* t) -{ - assert(!t->ctype); - assert(t->ty == Tdelegate); - assert(t->nextOf()->ty == Tfunction); - - IrFuncTy irFty; - llvm::Type* ltf = DtoFunctionType(t->nextOf(), irFty, NULL, - Type::tvoid->pointerTo()); - llvm::Type *types[] = { getVoidPtrType(), getPtrToType(ltf) }; - LLStructType* lt = LLStructType::get(gIR->context(), types, false); - - IrTypeDelegate* result = new IrTypeDelegate(t, lt, irFty); - t->ctype = result; - return result; +IrTypeDelegate::IrTypeDelegate(Type *dt, llvm::Type *lt, const IrFuncTy &irFty_) + : IrType(dt, lt), irFty(irFty_) {} + +IrTypeDelegate *IrTypeDelegate::get(Type *t) { + assert(!t->ctype); + assert(t->ty == Tdelegate); + assert(t->nextOf()->ty == Tfunction); + + IrFuncTy irFty; + llvm::Type *ltf = + DtoFunctionType(t->nextOf(), irFty, NULL, Type::tvoid->pointerTo()); + llvm::Type *types[] = {getVoidPtrType(), getPtrToType(ltf)}; + LLStructType *lt = LLStructType::get(gIR->context(), types, false); + + IrTypeDelegate *result = new IrTypeDelegate(t, lt, irFty); + t->ctype = result; + return result; } diff --git a/ir/irtypefunction.h b/ir/irtypefunction.h index f805e4ac72..ea4d55e8ea 100644 --- a/ir/irtypefunction.h +++ b/ir/irtypefunction.h @@ -19,43 +19,41 @@ struct IrFuncTy; /// -class IrTypeFunction : public IrType -{ +class IrTypeFunction : public IrType { public: - /// - static IrTypeFunction* get(Type* dt); + /// + static IrTypeFunction *get(Type *dt); - /// - IrTypeFunction* isFunction() { return this; } + /// + IrTypeFunction *isFunction() { return this; } - /// - IrFuncTy& getIrFuncTy() { return irFty; } + /// + IrFuncTy &getIrFuncTy() { return irFty; } protected: - /// - IrTypeFunction(Type* dt, llvm::Type* lt, const IrFuncTy& irFty); - /// - IrFuncTy irFty; + /// + IrTypeFunction(Type *dt, llvm::Type *lt, const IrFuncTy &irFty); + /// + IrFuncTy irFty; }; /// -class IrTypeDelegate : public IrType -{ +class IrTypeDelegate : public IrType { public: - /// - static IrTypeDelegate* get(Type* dt); + /// + static IrTypeDelegate *get(Type *dt); - /// - IrTypeDelegate* isDelegate() { return this; } + /// + IrTypeDelegate *isDelegate() { return this; } - /// - IrFuncTy& getIrFuncTy() { return irFty; } + /// + IrFuncTy &getIrFuncTy() { return irFty; } protected: - /// - IrTypeDelegate(Type* dt, LLType* lt, const IrFuncTy& irFty); - /// - IrFuncTy irFty; + /// + IrTypeDelegate(Type *dt, LLType *lt, const IrFuncTy &irFty); + /// + IrFuncTy irFty; }; #endif diff --git a/ir/irtypestruct.cpp b/ir/irtypestruct.cpp index e3b1f75ef7..2b6d30f7c1 100644 --- a/ir/irtypestruct.cpp +++ b/ir/irtypestruct.cpp @@ -24,42 +24,37 @@ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -IrTypeStruct::IrTypeStruct(StructDeclaration* sd) -: IrTypeAggr(sd), - sd(sd), - ts(static_cast(sd->type)) -{} +IrTypeStruct::IrTypeStruct(StructDeclaration *sd) + : IrTypeAggr(sd), sd(sd), ts(static_cast(sd->type)) {} ////////////////////////////////////////////////////////////////////////////// -IrTypeStruct* IrTypeStruct::get(StructDeclaration* sd) -{ - IrTypeStruct* t = new IrTypeStruct(sd); - sd->type->ctype = t; +IrTypeStruct *IrTypeStruct::get(StructDeclaration *sd) { + IrTypeStruct *t = new IrTypeStruct(sd); + sd->type->ctype = t; - IF_LOG Logger::println("Building struct type %s @ %s", - sd->toPrettyChars(), sd->loc.toChars()); - LOG_SCOPE; - - // if it's a forward declaration, all bets are off, stick with the opaque - if (sd->sizeok != SIZEOKdone) - return t; - - t->packed = sd->alignment == 1; - if (!t->packed) - { - // Unfortunately, the previous check is not enough in case the struct - // contains an align declaration. See issue 726. - t->packed = isPacked(sd); - } - - AggrTypeBuilder builder(t->packed); - builder.addAggregate(sd); - builder.addTailPadding(sd->structsize); - isaStruct(t->type)->setBody(builder.defaultTypes(), t->packed); - t->varGEPIndices = builder.varGEPIndices(); - - IF_LOG Logger::cout() << "final struct type: " << *t->type << std::endl; + IF_LOG Logger::println("Building struct type %s @ %s", sd->toPrettyChars(), + sd->loc.toChars()); + LOG_SCOPE; + // if it's a forward declaration, all bets are off, stick with the opaque + if (sd->sizeok != SIZEOKdone) return t; + + t->packed = sd->alignment == 1; + if (!t->packed) { + // Unfortunately, the previous check is not enough in case the struct + // contains an align declaration. See issue 726. + t->packed = isPacked(sd); + } + + AggrTypeBuilder builder(t->packed); + builder.addAggregate(sd); + builder.addTailPadding(sd->structsize); + isaStruct(t->type)->setBody(builder.defaultTypes(), t->packed); + t->varGEPIndices = builder.varGEPIndices(); + + IF_LOG Logger::cout() << "final struct type: " << *t->type << std::endl; + + return t; } diff --git a/ir/irtypestruct.h b/ir/irtypestruct.h index 159988bb9b..2b321e4f1b 100644 --- a/ir/irtypestruct.h +++ b/ir/irtypestruct.h @@ -16,24 +16,23 @@ class StructDeclaration; class TypeStruct; /// IrType for struct/union types. -class IrTypeStruct : public IrTypeAggr -{ +class IrTypeStruct : public IrTypeAggr { public: - /// - static IrTypeStruct* get(StructDeclaration* sd); + /// + static IrTypeStruct *get(StructDeclaration *sd); - /// - IrTypeStruct* isStruct() { return this; } + /// + IrTypeStruct *isStruct() { return this; } protected: - /// - IrTypeStruct(StructDeclaration* sd); + /// + IrTypeStruct(StructDeclaration *sd); - /// StructDeclaration this type represents. - StructDeclaration* sd = nullptr; + /// StructDeclaration this type represents. + StructDeclaration *sd = nullptr; - /// DMD TypeStruct of this type. - TypeStruct* ts = nullptr; + /// DMD TypeStruct of this type. + TypeStruct *ts = nullptr; }; #endif diff --git a/ir/irvar.cpp b/ir/irvar.cpp index 8efabf46c5..b12411be5f 100644 --- a/ir/irvar.cpp +++ b/ir/irvar.cpp @@ -17,108 +17,90 @@ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -IrVar *getIrVar(VarDeclaration *decl) -{ - assert(isIrVarCreated(decl)); - assert(decl->ir.irVar != NULL); - return decl->ir.irVar; +IrVar *getIrVar(VarDeclaration *decl) { + assert(isIrVarCreated(decl)); + assert(decl->ir.irVar != NULL); + return decl->ir.irVar; } -llvm::Value *getIrValue(VarDeclaration *decl) -{ - return getIrVar(decl)->value; -} +llvm::Value *getIrValue(VarDeclaration *decl) { return getIrVar(decl)->value; } -bool isIrVarCreated(VarDeclaration *decl) -{ - int t = decl->ir.type(); - bool isIrVar = t == IrDsymbol::GlobalType || - t == IrDsymbol::LocalType || - t == IrDsymbol::ParamterType || - t == IrDsymbol::FieldType; - assert(isIrVar || t == IrDsymbol::NotSet); - return isIrVar; +bool isIrVarCreated(VarDeclaration *decl) { + int t = decl->ir.type(); + bool isIrVar = t == IrDsymbol::GlobalType || t == IrDsymbol::LocalType || + t == IrDsymbol::ParamterType || t == IrDsymbol::FieldType; + assert(isIrVar || t == IrDsymbol::NotSet); + return isIrVar; } ////////////////////////////////////////////////////////////////////////////// -IrGlobal *getIrGlobal(VarDeclaration *decl, bool create) -{ - if (!isIrGlobalCreated(decl) && create) - { - assert(decl->ir.irGlobal == NULL); - decl->ir.irGlobal = new IrGlobal(decl); - decl->ir.m_type = IrDsymbol::GlobalType; - } - assert(decl->ir.irGlobal != NULL); - return decl->ir.irGlobal; +IrGlobal *getIrGlobal(VarDeclaration *decl, bool create) { + if (!isIrGlobalCreated(decl) && create) { + assert(decl->ir.irGlobal == NULL); + decl->ir.irGlobal = new IrGlobal(decl); + decl->ir.m_type = IrDsymbol::GlobalType; + } + assert(decl->ir.irGlobal != NULL); + return decl->ir.irGlobal; } -bool isIrGlobalCreated(VarDeclaration *decl) -{ - int t = decl->ir.type(); - assert(t == IrDsymbol::GlobalType || t == IrDsymbol::NotSet); - return t == IrDsymbol::GlobalType; +bool isIrGlobalCreated(VarDeclaration *decl) { + int t = decl->ir.type(); + assert(t == IrDsymbol::GlobalType || t == IrDsymbol::NotSet); + return t == IrDsymbol::GlobalType; } ////////////////////////////////////////////////////////////////////////////// -IrLocal *getIrLocal(VarDeclaration *decl, bool create) -{ - if (!isIrLocalCreated(decl) && create) - { - assert(decl->ir.irLocal == NULL); - decl->ir.irLocal = new IrLocal(decl); - decl->ir.m_type = IrDsymbol::LocalType; - } - assert(decl->ir.irLocal != NULL); - return decl->ir.irLocal; +IrLocal *getIrLocal(VarDeclaration *decl, bool create) { + if (!isIrLocalCreated(decl) && create) { + assert(decl->ir.irLocal == NULL); + decl->ir.irLocal = new IrLocal(decl); + decl->ir.m_type = IrDsymbol::LocalType; + } + assert(decl->ir.irLocal != NULL); + return decl->ir.irLocal; } -bool isIrLocalCreated(VarDeclaration *decl) -{ - int t = decl->ir.type(); - assert(t == IrDsymbol::LocalType || t == IrDsymbol::ParamterType || t == IrDsymbol::NotSet); - return t == IrDsymbol::LocalType || t == IrDsymbol::ParamterType; +bool isIrLocalCreated(VarDeclaration *decl) { + int t = decl->ir.type(); + assert(t == IrDsymbol::LocalType || t == IrDsymbol::ParamterType || + t == IrDsymbol::NotSet); + return t == IrDsymbol::LocalType || t == IrDsymbol::ParamterType; } ////////////////////////////////////////////////////////////////////////////// -IrParameter *getIrParameter(VarDeclaration *decl, bool create) -{ - if (!isIrParameterCreated(decl) && create) - { - assert(decl->ir.irParam == NULL); - decl->ir.irParam = new IrParameter(decl); - decl->ir.m_type = IrDsymbol::ParamterType; - } - return decl->ir.irParam; +IrParameter *getIrParameter(VarDeclaration *decl, bool create) { + if (!isIrParameterCreated(decl) && create) { + assert(decl->ir.irParam == NULL); + decl->ir.irParam = new IrParameter(decl); + decl->ir.m_type = IrDsymbol::ParamterType; + } + return decl->ir.irParam; } -bool isIrParameterCreated(VarDeclaration *decl) -{ - int t = decl->ir.type(); - assert(t == IrDsymbol::ParamterType || t == IrDsymbol::NotSet); - return t == IrDsymbol::ParamterType; +bool isIrParameterCreated(VarDeclaration *decl) { + int t = decl->ir.type(); + assert(t == IrDsymbol::ParamterType || t == IrDsymbol::NotSet); + return t == IrDsymbol::ParamterType; } ////////////////////////////////////////////////////////////////////////////// -IrField *getIrField(VarDeclaration *decl, bool create) -{ - if (!isIrFieldCreated(decl) && create) - { - assert(decl->ir.irField == NULL); - decl->ir.irField = new IrField(decl); - decl->ir.m_type = IrDsymbol::FieldType; - } - assert(decl->ir.irField != NULL); - return decl->ir.irField; +IrField *getIrField(VarDeclaration *decl, bool create) { + if (!isIrFieldCreated(decl) && create) { + assert(decl->ir.irField == NULL); + decl->ir.irField = new IrField(decl); + decl->ir.m_type = IrDsymbol::FieldType; + } + assert(decl->ir.irField != NULL); + return decl->ir.irField; } -bool isIrFieldCreated(VarDeclaration *decl) -{ - int t = decl->ir.type(); - assert(t == IrDsymbol::FieldType || t == IrDsymbol::NotSet); - return t == IrDsymbol::FieldType; +bool isIrFieldCreated(VarDeclaration *decl) { + int t = decl->ir.type(); + assert(t == IrDsymbol::FieldType || t == IrDsymbol::NotSet); + return t == IrDsymbol::FieldType; } diff --git a/ir/irvar.h b/ir/irvar.h index 755d210bf2..5e26104c45 100644 --- a/ir/irvar.h +++ b/ir/irvar.h @@ -21,81 +21,71 @@ struct IrFuncTyArg; class VarDeclaration; -struct IrVar -{ - explicit IrVar(VarDeclaration* var) - : V(var) {} - IrVar(VarDeclaration* var, llvm::Value* value) - : V(var), value(value) {} +struct IrVar { + explicit IrVar(VarDeclaration *var) : V(var) {} + IrVar(VarDeclaration *var, llvm::Value *value) : V(var), value(value) {} - VarDeclaration* V = nullptr; - llvm::Value* value = nullptr; + VarDeclaration *V = nullptr; + llvm::Value *value = nullptr; }; // represents a global variable -struct IrGlobal : IrVar -{ - explicit IrGlobal(VarDeclaration* v) - : IrVar(v) {} - IrGlobal(VarDeclaration* v, llvm::Type* type, llvm::Constant* constInit = nullptr) - : IrVar(v), type(type), constInit(constInit) {} +struct IrGlobal : IrVar { + explicit IrGlobal(VarDeclaration *v) : IrVar(v) {} + IrGlobal(VarDeclaration *v, llvm::Type *type, + llvm::Constant *constInit = nullptr) + : IrVar(v), type(type), constInit(constInit) {} - llvm::Type* type = nullptr; - llvm::Constant* constInit = nullptr; + llvm::Type *type = nullptr; + llvm::Constant *constInit = nullptr; - // This var is used by a naked function. - bool nakedUse = false; + // This var is used by a naked function. + bool nakedUse = false; }; // represents a local variable variable -struct IrLocal : IrVar -{ - explicit IrLocal(VarDeclaration* v) - : IrVar(v) {} - IrLocal(VarDeclaration* v, llvm::Value* value) - : IrVar(v, value) {} - IrLocal(VarDeclaration* v, int nestedDepth, int nestedIndex) - : IrVar(v), nestedDepth(nestedDepth), nestedIndex(nestedIndex) {} +struct IrLocal : IrVar { + explicit IrLocal(VarDeclaration *v) : IrVar(v) {} + IrLocal(VarDeclaration *v, llvm::Value *value) : IrVar(v, value) {} + IrLocal(VarDeclaration *v, int nestedDepth, int nestedIndex) + : IrVar(v), nestedDepth(nestedDepth), nestedIndex(nestedIndex) {} - // Used for hybrid nested context creation. - int nestedDepth = 0; - int nestedIndex = -1; + // Used for hybrid nested context creation. + int nestedDepth = 0; + int nestedIndex = -1; }; // represents a function parameter -struct IrParameter : IrLocal -{ - explicit IrParameter(VarDeclaration* v) - : IrLocal(v) {} - IrParameter(VarDeclaration* v, llvm::Value* value) - : IrLocal(v, value) {} - IrParameter(VarDeclaration* v, llvm::Value* value, IrFuncTyArg* arg, bool isVthis = false) - : IrLocal(v, value), arg(arg), isVthis(isVthis) {} +struct IrParameter : IrLocal { + explicit IrParameter(VarDeclaration *v) : IrLocal(v) {} + IrParameter(VarDeclaration *v, llvm::Value *value) : IrLocal(v, value) {} + IrParameter(VarDeclaration *v, llvm::Value *value, IrFuncTyArg *arg, + bool isVthis = false) + : IrLocal(v, value), arg(arg), isVthis(isVthis) {} - IrFuncTyArg* arg = nullptr; - bool isVthis = false; // true, if it is the 'this' parameter + IrFuncTyArg *arg = nullptr; + bool isVthis = false; // true, if it is the 'this' parameter }; // represents an aggregate field variable -struct IrField : IrVar -{ - explicit IrField(VarDeclaration* v) : IrVar(v) {}; +struct IrField : IrVar { + explicit IrField(VarDeclaration *v) : IrVar(v){}; }; -IrVar* getIrVar(VarDeclaration* decl); -llvm::Value* getIrValue(VarDeclaration* decl); -bool isIrVarCreated(VarDeclaration* decl); +IrVar *getIrVar(VarDeclaration *decl); +llvm::Value *getIrValue(VarDeclaration *decl); +bool isIrVarCreated(VarDeclaration *decl); -IrGlobal* getIrGlobal(VarDeclaration* decl, bool create = false); -bool isIrGlobalCreated(VarDeclaration* decl); +IrGlobal *getIrGlobal(VarDeclaration *decl, bool create = false); +bool isIrGlobalCreated(VarDeclaration *decl); -IrLocal* getIrLocal(VarDeclaration* decl, bool create = false); -bool isIrLocalCreated(VarDeclaration* decl); +IrLocal *getIrLocal(VarDeclaration *decl, bool create = false); +bool isIrLocalCreated(VarDeclaration *decl); -IrParameter* getIrParameter(VarDeclaration* decl, bool create = false); -bool isIrParameterCreated(VarDeclaration* decl); +IrParameter *getIrParameter(VarDeclaration *decl, bool create = false); +bool isIrParameterCreated(VarDeclaration *decl); -IrField* getIrField(VarDeclaration* decl, bool create = false); -bool isIrFieldCreated(VarDeclaration* decl); +IrField *getIrField(VarDeclaration *decl, bool create = false); +bool isIrFieldCreated(VarDeclaration *decl); #endif