Big rewrite / improvement of target handling.

If no CPU is specified, use the host CPU type, not just a default of "nehalem".
Provide better features strings to the LLVM target machinery.
 -> Thus ensuring that LLVM doesn't generate SSE>2 instructions for the SSE2
    target (Fixes issue #82).
 -> Slight code improvements from using cmovs in generated code now
Use the llvm popcnt intrinsic for the SSE2 target now (it now generates code
  that doesn't call the popcnt instruction now that we properly tell LLVM
  which instructions are and aren't available for SSE2.)
This commit is contained in:
Matt Pharr
2011-08-26 09:54:45 -07:00
parent c340ff3893
commit b67498766e
7 changed files with 286 additions and 184 deletions

View File

@@ -10,7 +10,12 @@ CLANG_LIBS = -lclangFrontend -lclangDriver \
-lclangSerialization -lclangParse -lclangSema \
-lclangAnalysis -lclangAST -lclangLex -lclangBasic
LLVM_LIBS=$(shell llvm-config --ldflags --libs) -lpthread -ldl
ISPC_LIBS=$(CLANG_LIBS) \
$(shell llvm-config --ldflags --libs backend bitreader bitwriter codegen engine mcjit scalaropts native analysis core instcombine ipa ipo linker instrumentation) \
-lpthread -ldl
ISPC_TEST_LIBS=$(shell llvm-config --ldflags --libs bitreader backend interpreter engine jit mcjit) \
-lpthread -ldl
LLVM_CXXFLAGS=$(shell llvm-config --cppflags)
LLVM_VERSION=$(shell llvm-config --version | sed s/\\./_/)
LLVM_VERSION_DEF=-DLLVM_$(LLVM_VERSION)
@@ -80,11 +85,11 @@ doxygen:
ispc: print_llvm_src dirs $(OBJS)
@echo Creating ispc executable
@$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(CLANG_LIBS) $(LLVM_LIBS)
@$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(ISPC_LIBS)
ispc_test: dirs ispc_test.cpp
@echo Creating ispc_test executable
@$(CXX) $(LDFLAGS) $(CXXFLAGS) -o $@ ispc_test.cpp $(LLVM_LIBS)
@$(CXX) $(LDFLAGS) $(CXXFLAGS) -o $@ ispc_test.cpp $(ISPC_TEST_LIBS)
objs/%.o: %.cpp
@echo Compiling $<

View File

@@ -277,41 +277,18 @@ define internal i32 @__max_uniform_uint32(i32, i32) nounwind readonly alwaysinli
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; horizontal ops / reductions
; FIXME: this is very inefficient, loops over all 32 bits...
; we could use the LLVM intrinsic declare i32 @llvm.ctpop.i32(i32),
; although that currently ends up generating a POPCNT instruction even
; if we give --target=sse2 on the command line. We probably need to
; pipe through the 'sse2' request to LLVM via the 'features' string
; at codegen time... (If e.g. --cpu=penryn is also passed along, then
; it does generate non-POPCNT code and in particular better code than
; the below does.)
declare i32 @llvm.ctpop.i32(i32)
declare i64 @llvm.ctpop.i64(i64)
define internal i32 @__popcnt_int32(i32) nounwind readonly alwaysinline {
entry:
br label %loop
loop:
%count = phi i32 [ 0, %entry ], [ %newcount, %loop ]
%val = phi i32 [ %0, %entry ], [ %newval, %loop ]
%delta = and i32 %val, 1
%newcount = add i32 %count, %delta
%newval = lshr i32 %val, 1
%done = icmp eq i32 %newval, 0
br i1 %done, label %exit, label %loop
exit:
ret i32 %newcount
%val = call i32 @llvm.ctpop.i32(i32 %0)
ret i32 %val
}
define internal i32 @__popcnt_int64(i64) nounwind readnone alwaysinline {
%vec = bitcast i64 %0 to <2 x i32>
%v0 = extractelement <2 x i32> %vec, i32 0
%v1 = extractelement <2 x i32> %vec, i32 1
%c0 = call i32 @__popcnt_int32(i32 %v0)
%c1 = call i32 @__popcnt_int32(i32 %v1)
%sum = add i32 %c0, %c1
ret i32 %sum
%val = call i64 @llvm.ctpop.i64(i64 %0)
%val32 = trunc i64 %val to i32
ret i32 %val32
}

195
ispc.cpp
View File

@@ -50,6 +50,18 @@
#endif
#include <llvm/Analysis/DebugInfo.h>
#include <llvm/Support/Dwarf.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm/Target/TargetData.h>
#if defined(LLVM_3_0) || defined(LLVM_3_0svn)
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Support/TargetSelect.h>
#else
#include <llvm/Target/TargetRegistry.h>
#include <llvm/Target/TargetSelect.h>
#include <llvm/Target/SubtargetFeature.h>
#endif
#include <llvm/Support/Host.h>
Globals *g;
Module *m;
@@ -57,15 +69,184 @@ Module *m;
///////////////////////////////////////////////////////////////////////////
// Target
Target::Target() {
arch = "x86-64";
cpu = "nehalem";
is32bit = false;
isa = SSE4;
nativeVectorWidth = 4;
vectorWidth = 4;
bool
Target::GetTarget(const char *arch, const char *cpu, const char *isa,
Target *t) {
// initialize available targets
LLVMInitializeX86Target();
LLVMInitializeX86TargetInfo();
if (cpu == NULL) {
std::string hostCPU = llvm::sys::getHostCPUName();
if (hostCPU.size() > 0)
cpu = hostCPU.c_str();
else {
fprintf(stderr, "Warning: unable to determine host CPU!\n");
cpu = "generic";
}
}
t->cpu = cpu;
if (isa == NULL) {
if (!strcasecmp(cpu, "atom"))
isa = "sse2";
#if defined(LLVM_3_0) || defined(LLVM_3_0_svn)
else if (!strcasecmp(cpu, "sandybridge") ||
!strcasecmp(cpu, "corei7-avx"))
isa = "avx";
#endif // LLVM_3_0
else
isa = "sse4";
}
if (arch == NULL)
arch = "x86-64";
bool error = false;
// Make sure the target architecture is a known one; print an error
// with the valid ones otherwise.
t->target = NULL;
for (llvm::TargetRegistry::iterator iter = llvm::TargetRegistry::begin();
iter != llvm::TargetRegistry::end(); ++iter) {
if (std::string(arch) == iter->getName()) {
t->target = &*iter;
break;
}
}
if (t->target == NULL) {
fprintf(stderr, "Invalid architecture \"%s\"\nOptions: ", arch);
llvm::TargetRegistry::iterator iter;
for (iter = llvm::TargetRegistry::begin();
iter != llvm::TargetRegistry::end(); ++iter)
fprintf(stderr, "%s ", iter->getName());
fprintf(stderr, "\n");
error = true;
}
else {
t->arch = arch;
}
if (!strcasecmp(isa, "sse2")) {
t->isa = Target::SSE2;
t->nativeVectorWidth = 4;
t->vectorWidth = 4;
t->attributes = "-sse2,-sse41,-sse42,-sse4a,-ssse3,-popcnt";
}
else if (!strcasecmp(isa, "sse4")) {
t->isa = Target::SSE4;
t->nativeVectorWidth = 4;
t->vectorWidth = 4;
t->attributes = "+sse,+sse2,+sse3,+sse41,+sse42,+sse4a,+ssse3,+popcnt,+cmov";
}
else if (!strcasecmp(isa, "sse4x2")) {
t->isa = Target::SSE4;
t->nativeVectorWidth = 4;
t->vectorWidth = 8;
t->attributes = "+sse,+sse2,+sse3,+sse41,+sse42,+sse4a,+ssse3,+popcnt,+cmov";
}
#if defined(LLVM_3_0) || defined(LLVM_3_0svn)
else if (!strcasecmp(isa, "avx")) {
t->isa = Target::AVX;
t->nativeVectorWidth = 8;
t->vectorWidth = 8;
t->attributes = "+avx,+popcnt,+cmov";
}
else if (!strcasecmp(isa, "avx-x2")) {
t->isa = Target::AVX;
t->nativeVectorWidth = 8;
t->vectorWidth = 16;
t->attributes = "+avx,+popcnt,+cmov";
}
#endif // LLVM 3.0
else {
fprintf(stderr, "Target ISA \"%s\" is unknown. Choices are: %s\n",
isa, SupportedTargetISAs());
error = true;
}
if (!error) {
llvm::TargetMachine *targetMachine = t->GetTargetMachine();
const llvm::TargetData *targetData = targetMachine->getTargetData();
t->is32bit = (targetData->getPointerSize() == 4);
}
return !error;
}
const char *
Target::SupportedTargetCPUs() {
return "atom, barcelona, core2, corei7, "
#if defined(LLVM_3_0) || defined(LLVM_3_0_svn)
"corei7-avx, "
#endif
"istanbul, nocona, penryn, "
#ifdef LLVM_2_9
"sandybridge, "
#endif
"westmere";
}
const char *
Target::SupportedTargetArchs() {
return "x86, x86-64";
}
const char *
Target::SupportedTargetISAs() {
return "sse2, sse4, sse4x2"
#if defined(LLVM_3_0) || defined(LLVM_3_0_svn)
", avx, avx-x2"
#endif
;
}
std::string
Target::GetTripleString() const {
llvm::Triple triple;
// Start with the host triple as the default
triple.setTriple(llvm::sys::getHostTriple());
// And override the arch in the host triple based on what the user
// specified. Here we need to deal with the fact that LLVM uses one
// naming convention for targets TargetRegistry, but wants some
// slightly different ones for the triple. TODO: is there a way to
// have it do this remapping, which would presumably be a bit less
// error prone?
if (arch == "x86")
triple.setArchName("i386");
else if (arch == "x86-64")
triple.setArchName("x86_64");
else
triple.setArchName(arch);
return triple.str();
}
llvm::TargetMachine *
Target::GetTargetMachine() const {
std::string triple = GetTripleString();
#if defined(LLVM_3_0svn) || defined(LLVM_3_0)
std::string featuresString = attributes;
llvm::TargetMachine *targetMachine =
target->createTargetMachine(triple, cpu, featuresString);
#else
std::string featuresString = cpu + std::string(",") + attributes;
llvm::TargetMachine *targetMachine =
target->createTargetMachine(triple, featuresString);
#endif
assert(targetMachine != NULL);
targetMachine->setAsmVerbosityDefault(true);
return targetMachine;
}
///////////////////////////////////////////////////////////////////////////
// Opt

34
ispc.h
View File

@@ -69,6 +69,8 @@ namespace llvm {
class FunctionType;
class LLVMContext;
class Module;
class Target;
class TargetMachine;
class Type;
class Value;
}
@@ -156,7 +158,34 @@ public:
This structure defines a compilation target for the ispc compiler.
*/
struct Target {
Target();
/** Initializes the given Target pointer for a target of the given
name, if the name is a known target. Returns true if the
target was initialized and false if the name is unknown. */
static bool GetTarget(const char *arch, const char *cpu, const char *isa,
Target *);
/** Returns a comma-delimited string giving the names of the currently
supported target ISAs. */
static const char *SupportedTargetISAs();
/** Returns a comma-delimited string giving the names of the currently
supported target CPUs. */
static const char *SupportedTargetCPUs();
/** Returns a comma-delimited string giving the names of the currently
supported target architectures. */
static const char *SupportedTargetArchs();
/** Returns a triple string specifying the target architecture, vendor,
and environment. */
std::string GetTripleString() const;
/** Returns the LLVM TargetMachine object corresponding to this
target. */
llvm::TargetMachine *GetTargetMachine() const;
/** llvm Target object representing this target. */
const llvm::Target *target;
/** Enumerator giving the instruction sets that the compiler can
target. */
@@ -174,6 +203,9 @@ struct Target {
/** Target CPU. (e.g. "corei7", "corei7-avx", ..) */
std::string cpu;
/** Target-specific attributes to pass along to the LLVM backend */
std::string attributes;
/** Native vector width of the vector instruction set. Note that this
value is directly derived from the ISA Being used (e.g. it's 4 for
SSE, 8 for AVX, etc.) */

View File

@@ -171,7 +171,21 @@ static bool lRunTest(const char *fn) {
}
std::string eeError;
#if defined(LLVM_3_0) || defined(LLVM_3_0svn)
llvm::EngineBuilder engineBuilder(module);
engineBuilder.setErrorStr(&eeError);
engineBuilder.setEngineKind(llvm::EngineKind::JIT);
#if 0
std::vector<std::string> attributes;
if (target != NULL && !strcmp(target, "avx"))
attributes.push_back("+avx");
engineBuilder.setMAttrs(attributes);
engineBuilder.setUseMCJIT(true);
#endif
llvm::ExecutionEngine *ee = engineBuilder.create();
#else
llvm::ExecutionEngine *ee = llvm::ExecutionEngine::createJIT(module, &eeError);
#endif
if (!ee) {
fprintf(stderr, "Unable to create ExecutionEngine: %s\n", eeError.c_str());
return false;
@@ -338,6 +352,11 @@ static bool lRunTest(const char *fn) {
int main(int argc, char *argv[]) {
llvm::InitializeNativeTarget();
#if defined(LLVM_3_0) || defined(LLVM_3_0svn)
llvm::InitializeAllTargetMCs();
LLVMLinkInMCJIT();
LLVMLinkInJIT();
#endif
std::vector<const char *> files;
for (int i = 1; i < argc; ++i) {

View File

@@ -53,12 +53,13 @@
#endif // ISPC_IS_WINDOWS
static void usage(int ret) {
printf("This is the Intel(r) SPMD Program Compiler (ispc), build %s (%s)\n\n", BUILD_DATE, BUILD_VERSION);
printf("This is the Intel(r) SPMD Program Compiler (ispc), build %s (%s)\n\n",
BUILD_DATE, BUILD_VERSION);
printf("usage: ispc\n");
printf(" [--arch={x86,x86-64}]\t\tSelect target architecture\n");
printf(" [--arch={%s}]\t\tSelect target architecture\n",
Target::SupportedTargetArchs());
printf(" [--cpu=<cpu>]\t\t\tSelect target CPU type\n");
printf(" (atom, barcelona, core2, corei7, corei7-avx, istanbul, nocona,\n");
printf(" penryn, westmere)\n");
printf(" (%s)\n", Target::SupportedTargetCPUs());
#ifndef ISPC_IS_WINDOWS
printf(" [-D<foo>]\t\t\t\t#define value when running preprocessor\n");
#endif
@@ -91,11 +92,7 @@ static void usage(int ret) {
printf(" disable-gather-scatter-flattening\tDisable flattening when all lanes are on\n");
printf(" disable-uniform-memory-optimizations\tDisable uniform-based coherent memory access\n");
printf(" disable-masked-store-optimizations\tDisable lowering to regular stores when possible\n");
#if defined(LLVM_3_0) || defined(LLVM_3_0svn)
printf(" [--target={sse2,sse4,sse4x2,avx,avx-x2}] Select target ISA (SSE4 is default unless compiling for atom; then SSE2 is.)\n");
#else
printf(" [--target={sse2,sse4,sse4x2}] Select target ISA (SSE4 is default unless compiling for atom; then SSE2 is.)\n");
#endif // LLVM 3.0
printf(" [--target=<isa>]\t\t\tSelect target ISA. (%s)\n", Target::SupportedTargetISAs());
printf(" [--version]\t\t\t\tPrint ispc version\n");
printf(" [--woff]\t\t\t\tDisable warnings\n");
printf(" [--wno-perf]\t\t\tDon't issue warnings related to performance-related issues\n");
@@ -103,40 +100,6 @@ static void usage(int ret) {
exit(ret);
}
/** Given a target name string, set initialize the global g->target
structure appropriately.
*/
static void lDoTarget(const char *target) {
if (!strcasecmp(target, "sse2")) {
g->target.isa = Target::SSE2;
g->target.nativeVectorWidth = 4;
g->target.vectorWidth = 4;
}
else if (!strcasecmp(target, "sse4")) {
g->target.isa = Target::SSE4;
g->target.nativeVectorWidth = 4;
g->target.vectorWidth = 4;
}
else if (!strcasecmp(target, "sse4x2")) {
g->target.isa = Target::SSE4;
g->target.nativeVectorWidth = 4;
g->target.vectorWidth = 8;
}
#if defined(LLVM_3_0) || defined(LLVM_3_0svn)
else if (!strcasecmp(target, "avx")) {
g->target.isa = Target::AVX;
g->target.nativeVectorWidth = 8;
g->target.vectorWidth = 8;
}
else if (!strcasecmp(target, "avx-x2")) {
g->target.isa = Target::AVX;
g->target.nativeVectorWidth = 8;
g->target.vectorWidth = 16;
}
#endif // LLVM 3.0
else
usage(1);
}
/** We take arguments from both the command line as well as from the
@@ -203,9 +166,10 @@ int main(int Argc, char *Argv[]) {
// as we're parsing below
g = new Globals;
bool debugSet = false, optSet = false, targetSet = false;
bool debugSet = false, optSet = false;
Module::OutputType ot = Module::Object;
const char *arch = NULL, *cpu = NULL, *target = NULL;
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--help"))
usage(0);
@@ -214,15 +178,10 @@ int main(int Argc, char *Argv[]) {
g->cppArgs.push_back(argv[i]);
}
#endif // !ISPC_IS_WINDOWS
else if (!strncmp(argv[i], "--arch=", 7)) {
g->target.arch = argv[i] + 7;
if (g->target.arch == "x86")
g->target.is32bit = true;
else if (g->target.arch == "x86-64")
g->target.is32bit = false;
}
else if (!strncmp(argv[i], "--arch=", 7))
arch = argv[i] + 7;
else if (!strncmp(argv[i], "--cpu=", 6))
g->target.cpu = argv[i] + 6;
cpu = argv[i] + 6;
else if (!strcmp(argv[i], "--fast-math"))
g->opt.fastMath = true;
else if (!strcmp(argv[i], "--debug"))
@@ -240,14 +199,12 @@ int main(int Argc, char *Argv[]) {
else if (!strcmp(argv[i], "--emit-obj"))
ot = Module::Object;
else if (!strcmp(argv[i], "--target")) {
// FIXME: should remove this way of specifying the target...
if (++i == argc) usage(1);
lDoTarget(argv[i]);
targetSet = true;
}
else if (!strncmp(argv[i], "--target=", 9)) {
const char *target = argv[i] + 9;
lDoTarget(target);
target = argv[i];
}
else if (!strncmp(argv[i], "--target=", 9))
target = argv[i] + 9;
else if (!strncmp(argv[i], "--math-lib=", 11)) {
const char *lib = argv[i] + 11;
if (!strcmp(lib, "default"))
@@ -332,10 +289,8 @@ int main(int Argc, char *Argv[]) {
if (debugSet && !optSet)
g->opt.level = 0;
// Make SSE2 the default target on atom unless the target has been set
// explicitly.
if (!targetSet && (g->target.cpu == "atom"))
lDoTarget("sse2");
if (!Target::GetTarget(arch, cpu, target, &g->target))
usage(1);
m = new Module(file);
if (m->CompileFile() == 0) {

View File

@@ -72,18 +72,16 @@
#include <llvm/Support/FormattedStream.h>
#include <llvm/Support/FileUtilities.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm/Target/TargetData.h>
#if defined(LLVM_3_0) || defined(LLVM_3_0svn)
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Support/TargetSelect.h>
#else
#include <llvm/Target/TargetRegistry.h>
#include <llvm/Target/TargetSelect.h>
#endif
#include <llvm/Target/TargetOptions.h>
#include <llvm/Target/TargetData.h>
#if !defined(LLVM_3_0) && !defined(LLVM_3_0svn)
#include <llvm/Target/SubtargetFeature.h>
#endif // !LLVM_3_0
#endif
#include <llvm/PassManager.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Support/CFG.h>
@@ -91,10 +89,10 @@
#include <clang/Frontend/Utils.h>
#include <clang/Basic/TargetInfo.h>
#ifndef LLVM_2_8
#include <llvm/Support/ToolOutputFile.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/ToolOutputFile.h>
#include <llvm/Support/Host.h>
#else // !LLVM_2_8
#include <llvm/System/Host.h>
#include <llvm/System/Host.h>
#endif // LLVM_2_8
#include <llvm/Assembly/PrintModulePass.h>
#include <llvm/Support/raw_ostream.h>
@@ -114,42 +112,7 @@ Module::Module(const char *fn) {
symbolTable = new SymbolTable;
module = new llvm::Module(filename ? filename : "<stdin>", *g->ctx);
// initialize target in module
llvm::InitializeAllTargets();
llvm::Triple triple;
// Start with the host triple as the default
triple.setTriple(llvm::sys::getHostTriple());
if (g->target.arch != "") {
// If the user specified a target architecture, see if it's a known
// one; print an error with the valid ones otherwise.
const llvm::Target *target = NULL;
for (llvm::TargetRegistry::iterator iter = llvm::TargetRegistry::begin();
iter != llvm::TargetRegistry::end(); ++iter) {
if (g->target.arch == iter->getName()) {
target = &*iter;
break;
}
}
if (!target) {
fprintf(stderr, "Invalid target \"%s\"\nOptions: ",
g->target.arch.c_str());
llvm::TargetRegistry::iterator iter;
for (iter = llvm::TargetRegistry::begin();
iter != llvm::TargetRegistry::end(); ++iter)
fprintf(stderr, "%s ", iter->getName());
fprintf(stderr, "\n");
exit(1);
}
// And override the arch in the host triple
llvm::Triple::ArchType archType =
llvm::Triple::getArchTypeForLLVMName(g->target.arch);
if (archType != llvm::Triple::UnknownArch)
triple.setArch(archType);
}
module->setTargetTriple(triple.str());
module->setTargetTriple(g->target.GetTripleString());
#ifndef LLVM_2_8
if (g->generateDebuggingSymbols)
@@ -973,42 +936,12 @@ Module::WriteOutput(OutputType outputType, const char *outFileName) {
bool
Module::writeObjectFileOrAssembly(OutputType outputType, const char *outFileName) {
#if defined(LLVM_3_0) || defined(LLVM_3_0svn)
llvm::InitializeAllTargetMCs();
LLVMInitializeX86TargetMC();
#endif
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllAsmParsers();
LLVMInitializeX86AsmPrinter();
LLVMInitializeX86AsmParser();
llvm::Triple triple(module->getTargetTriple());
assert(triple.getTriple().empty() == false);
const llvm::Target *target = NULL;
std::string error;
target = llvm::TargetRegistry::lookupTarget(triple.getTriple(), error);
assert(target != NULL);
std::string featuresString;
llvm::TargetMachine *targetMachine = NULL;
#if defined LLVM_3_0svn || defined LLVM_3_0
if (g->target.isa == Target::AVX)
featuresString = "+avx";
targetMachine = target->createTargetMachine(triple.getTriple(), g->target.cpu,
featuresString);
#else
if (g->target.cpu.size()) {
llvm::SubtargetFeatures features;
features.setCPU(g->target.cpu);
featuresString = features.getString();
}
targetMachine = target->createTargetMachine(triple.getTriple(),
featuresString);
#endif
if (targetMachine == NULL) {
fprintf(stderr, "Unable to create target machine for target \"%s\"!",
triple.str().c_str());
return false;
}
targetMachine->setAsmVerbosityDefault(true);
llvm::TargetMachine *targetMachine = g->target.GetTargetMachine();
// Figure out if we're generating object file or assembly output, and
// set binary output for object files
@@ -1017,6 +950,7 @@ Module::writeObjectFileOrAssembly(OutputType outputType, const char *outFileName
bool binary = (fileType == llvm::TargetMachine::CGFT_ObjectFile);
unsigned int flags = binary ? llvm::raw_fd_ostream::F_Binary : 0;
std::string error;
llvm::tool_output_file *of = new llvm::tool_output_file(outFileName, error, flags);
if (error.size()) {
fprintf(stderr, "Error opening output file \"%s\".\n", outFileName);
@@ -1034,9 +968,8 @@ Module::writeObjectFileOrAssembly(OutputType outputType, const char *outFileName
(g->opt.level > 0) ? llvm::CodeGenOpt::Aggressive : llvm::CodeGenOpt::None;
if (targetMachine->addPassesToEmitFile(pm, fos, fileType, optLevel)) {
fprintf(stderr, "Fatal error adding passes to emit object file for "
"target %s!\n", triple.str().c_str());
return false;
fprintf(stderr, "Fatal error adding passes to emit object file!");
exit(1);
}
// Finally, run the passes to emit the object file/assembly