[LLVMdev] Newbie JITter

Antony Blakey
Thu, 08 Nov 2007 02:10:10 -0000

Hi,
    I'm experimenting with using LLVM to generate dynamic FFI bridges
in VisualWorks Smalltalk. LLVM is an amazing thing! I'm going from
dynamically generated assembler source to machine code, and I have
that all working, copied from the llc tool and the JIT example. I
have two questions:

1. What optimization passes, if any, should I run on the module
before I pass it to the ExecutionEngine.
2. Do I need to retain the Module/ExistingModuleProvider, once I've
built the ExecutionEngine and have a Function object. Or is there
some simpler way to store/call the native code block?

An unrelated question: I want to dump the native assembler form of
the code generated by the JIT. Currently I dump it using some code I
adapted from llc, but that's not actually showing what the JIT is
generating. How can I dump the JIT generated code, as native
assembler? It's a debugging requirement only.

My test code is as follows, much of which is to do with generating
the native assembler output.

#include "llvm/Module.h"
#include "llvm/Assembly/Parser.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/ModuleProvider.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/System/Signals.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/PassManager.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetMachineRegistry.h"
#include <iostream>
using namespace llvm;

int main() {
	sys::PrintStackTraceOnErrorSignal();

	const char* assembler "@.LC0 = internal constant [19 x i8] c\"cooking
with gas!\\0A\\00
\"     ; [11 x i8]*\n"
"\n"
"declare i32 @puts(i8 *)                                          ;
i32(i8 *)* \n"
"\n"
"define i32 @main() {                                             ;
i32()* \n"
"        ; Convert [19 x i8 ]* to i8 *...\n"
"        %cast210 = getelementptr [19 x i8]* @.LC0, i64 0, i64 0 ; i8
*\n"
"        call i32 @puts(i8 * %cast210)                            ;
i32\n"
"        ret i32 0\n"
"};\n";

	int exitCode = 0;
	try {
		ParseError ParseErr;
		Module* M = ParseAssemblyString(assembler, new Module("test"),
&ParseErr);
		if (!M) {
			cerr << "parse error: " << ParseErr.getMessage()
<< "\n";
			return 1;
		}

		std::cout << "\nWe just constructed this LLVM module:\n\n"
<< *M;

		std::string MArchErr;
		const TargetMachineRegistry::entry* MArch =
TargetMachineRegistry::getClosestStaticTargetForModule(*M, MArchErr);
		if (MArch == 0) {
			std::cerr << "error auto-selecting target for module '"
<<
MArchErr << "'.\n";
			return 1;
		}

		TargetMachine* target = MArch->CtorFn(*M, "");
		assert(target && "Could not allocate target machine!");

		FunctionPassManager Passes(new ExistingModuleProvider(M));
		Passes.add(new TargetData(*target->getTargetData()));

		Passes.add(createVerifierPass());

		switch (target->addPassesToEmitFile(Passes, std::cout,
TargetMachine::AssemblyFile, false)) {
			default:
				assert(0 && "Invalid file model!");
				return 1;
			case FileModel::MachOFile:
			case FileModel::ElfFile:
			case FileModel::Error:
				std::cerr << "target does not support generation of this file
type!\n";
				return 1;
			case FileModel::AsmFile:
				break;
		}

		MachineCodeEmitter *MCE = 0;
		if (target->addPassesToEmitFileFinish(Passes, MCE, false)) {
			std::cerr << "target does not support generation of this file
type!
\n";
			return 1;
		}

		std::cout << "\nWhich has this machine code form:";

		Passes.doInitialization();

		for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
			if (!I->isDeclaration())
				Passes.run(*I);

		Passes.doFinalization();

		ExistingModuleProvider* MP = new ExistingModuleProvider(M);
		ExecutionEngine* EE = ExecutionEngine::create(MP, false);

		Function *MainFunction = M->getFunction("main");
		if (!MainFunction) {
			std::cerr << "'main' function not found in
module.\n";
			return -1;
		}

		std::cout << "\n\nRunning main: " << std::flush;

		// Call the function with no arguments:
		std::vector<GenericValue> noargs;
		GenericValue gv = EE->runFunction(MainFunction, noargs);

		// Import result of execution:
		std::cout << "Result: " <<
gv.IntVal.toStringUnsigned(10) << "\n";

	} catch (const std::string& msg) {
		cerr << "exception: " << msg << "\n";
		exitCode = 1;
	} catch (...) {
		cerr << "exception: Unexpected unknown exception occurred.\n";
		exitCode = 1;
	}

	return exitCode;
}

Thanks,

Antony Blakey
--------------------------
CTO, Linkuistics Pty Ltd
Ph: 0438 840 787

Plurality is not to be assumed without necessity
   -- William of Ockham (ca. 1285-1349)