Add a minimal C interpreter example.

- Demonstrates how to build a standalone tool which loads source code using the
   Driver and Frontend libraries, and then uses CodeGen and the JIT to actually
   execute the code.

 - Still more complicated than it should be, but hey its only 153 lines. :)

--
ddunbar@ozzy:tmp$ cat hello.c
#include <stdio.h>
int main() { printf("hello world\n"); return 0; }
ddunbar@ozzy:tmp$ clang-interpreter hello.c
hello world
--

llvm-svn: 97133
This commit is contained in:
Daniel Dunbar 2010-02-25 08:49:05 +00:00
parent a72e1af8bf
commit 0076d94466
4 changed files with 198 additions and 1 deletions

View File

@ -9,6 +9,6 @@
LEVEL = ../../..
PARALLEL_DIRS := PrintFunctionNames wpa
PARALLEL_DIRS := clang-interpreter PrintFunctionNames wpa
include $(LEVEL)/Makefile.common

View File

@ -0,0 +1,28 @@
##===- examples/clang-interpreter/Makefile -----------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
LEVEL = ../../../..
TOOLNAME = clang-interpreter
CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include
NO_INSTALL = 1
TOOL_NO_EXPORTS = 1
# Include this here so we can get the configuration of the targets that have
# been configured for construction. We have to do this early so we can set up
# LINK_COMPONENTS before including Makefile.rules
include $(LEVEL)/Makefile.config
LINK_COMPONENTS := jit interpreter nativecodegen bitreader bitwriter ipo \
selectiondag
USEDLIBS = clangFrontend.a clangDriver.a clangCodeGen.a clangSema.a \
clangChecker.a clangAnalysis.a clangRewrite.a clangAST.a \
clangParse.a clangLex.a clangBasic.a
include $(LLVM_SRC_ROOT)/Makefile.rules

View File

@ -0,0 +1,17 @@
This is an example of Clang based interpreter, for executing standalone C
programs.
It demonstrates the following features:
1. Parsing standard compiler command line arguments using the Driver library.
2. Constructing a Clang compiler instance, using the appropriate arguments
derived in step #1.
3. Invoking the Clang compiler to lex, parse, syntax check, and then generate
LLVM code.
4. Use the LLVM JIT functionality to execute the final module.
The implementation has many limitations and is not designed to be a full fledged
C interpreter. It is designed to demonstrate a simple but functional use of the
Clang compiler libraries.

View File

@ -0,0 +1,152 @@
//===-- examples/clang-interpreter/main.cpp - Clang C Interpreter Example -===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CodeGenAction.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Config/config.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Config/config.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/System/Host.h"
#include "llvm/System/Path.h"
#include "llvm/Target/TargetSelect.h"
using namespace clang;
using namespace clang::driver;
llvm::sys::Path GetExecutablePath(const char *Argv0) {
// This just needs to be some symbol in the binary; C++ doesn't
// allow taking the address of ::main however.
void *MainAddr = (void*) (intptr_t) GetExecutablePath;
return llvm::sys::Path::GetMainExecutable(Argv0, MainAddr);
}
int Execute(llvm::Module *Mod, char * const *envp) {
llvm::InitializeNativeTarget();
std::string Error;
llvm::OwningPtr<llvm::ExecutionEngine> EE(
llvm::ExecutionEngine::createJIT(Mod, &Error));
if (!EE) {
llvm::errs() << "unable to make execution engine: " << Error << "\n";
return 255;
}
llvm::Function *EntryFn = Mod->getFunction("main");
if (!EntryFn) {
llvm::errs() << "'main' function not found in module.\n";
return 255;
}
// FIXME: Support passing arguments.
std::vector<std::string> Args;
Args.push_back(Mod->getModuleIdentifier());
return EE->runFunctionAsMain(EntryFn, Args, envp);
}
int main(int argc, const char **argv, char * const *envp) {
void *MainAddr = (void*) (intptr_t) GetExecutablePath;
llvm::sys::Path Path = GetExecutablePath(argv[0]);
TextDiagnosticPrinter DiagClient(llvm::errs(), DiagnosticOptions());
Diagnostic Diags(&DiagClient);
Driver TheDriver(Path.getBasename(), Path.getDirname(),
llvm::sys::getHostTriple(),
"a.out", /*IsProduction=*/false, Diags);
TheDriver.setTitle("clang interpreter");
// FIXME: This is a hack to try to force the driver to do something we can
// recognize. We need to extend the driver library to support this use model
// (basically, exactly one input, and the operation mode is hard wired).
llvm::SmallVector<const char *, 16> Args(argv, argv + argc);
Args.push_back("-fsyntax-only");
llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args.size(),
Args.data()));
if (!C)
return 0;
// FIXME: This is copied from ASTUnit.cpp; simplify and eliminate.
// We expect to get back exactly one command job, if we didn't something
// failed. Extract that job from the compilation.
const driver::JobList &Jobs = C->getJobs();
if (Jobs.size() != 1 || !isa<driver::Command>(Jobs.begin())) {
llvm::SmallString<256> Msg;
llvm::raw_svector_ostream OS(Msg);
C->PrintJob(OS, C->getJobs(), "; ", true);
Diags.Report(diag::err_fe_expected_compiler_job) << OS.str();
return 1;
}
const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") {
Diags.Report(diag::err_fe_expected_clang_command);
return 1;
}
// Initialize a compiler invocation object from the clang (-cc1) arguments.
const driver::ArgStringList &CCArgs = Cmd->getArguments();
llvm::OwningPtr<CompilerInvocation> CI(new CompilerInvocation);
CompilerInvocation::CreateFromArgs(*CI, (const char**) CCArgs.data(),
(const char**) CCArgs.data()+CCArgs.size(),
Diags);
// Show the invocation, with -v.
if (CI->getHeaderSearchOpts().Verbose) {
llvm::errs() << "clang invocation:\n";
C->PrintJob(llvm::errs(), C->getJobs(), "\n", true);
llvm::errs() << "\n";
}
// FIXME: This is copied from cc1_main.cpp; simplify and eliminate.
// Create a compiler instance to handle the actual work.
CompilerInstance Clang;
Clang.setLLVMContext(new llvm::LLVMContext);
Clang.setInvocation(CI.take());
// Create the compilers actual diagnostics engine.
Clang.createDiagnostics(int(CCArgs.size()), (char**) CCArgs.data());
if (!Clang.hasDiagnostics())
return 1;
// Infer the builtin include path if unspecified.
if (Clang.getHeaderSearchOpts().UseBuiltinIncludes &&
Clang.getHeaderSearchOpts().ResourceDir.empty())
Clang.getHeaderSearchOpts().ResourceDir =
CompilerInvocation::GetResourcesPath(argv[0], MainAddr);
// Create and execute the frontend to generate an LLVM bitcode module.
llvm::OwningPtr<CodeGenAction> Act(new EmitLLVMOnlyAction());
if (!Clang.ExecuteAction(*Act))
return 1;
int Res = 255;
if (llvm::Module *Module = Act->takeModule())
Res = Execute(Module, envp);
// Shutdown.
llvm::llvm_shutdown();
return Res;
}