mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-30 02:32:08 +00:00
37b25ab2a9
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@112465 91177308-0d34-0410-b5e6-96231b3b80d8
352 lines
13 KiB
C++
352 lines
13 KiB
C++
//===-- BrainFCodeGen.cpp - BrainF trace compiler -----------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
#include "BrainF.h"
|
|
#include "BrainFVM.h"
|
|
#include "llvm/Attributes.h"
|
|
#include "llvm/Support/StandardPasses.h"
|
|
#include "llvm/Target/TargetData.h"
|
|
#include "llvm/Target/TargetSelect.h"
|
|
#include "llvm/Transforms/Scalar.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
|
|
/// initialize_module - perform setup of the LLVM code generation system.
|
|
void BrainFTraceRecorder::initialize_module() {
|
|
LLVMContext &Context = module->getContext();
|
|
|
|
// Initialize the code generator, and enable aggressive code generation.
|
|
InitializeNativeTarget();
|
|
EngineBuilder builder(module);
|
|
builder.setOptLevel(CodeGenOpt::Aggressive);
|
|
EE = builder.create();
|
|
|
|
// Create a FunctionPassManager to handle running optimization passes
|
|
// on our generated code. Setup a basic suite of optimizations for it.
|
|
FPM = new llvm::FunctionPassManager(module);
|
|
FPM->add(createInstructionCombiningPass());
|
|
FPM->add(createCFGSimplificationPass());
|
|
FPM->add(createScalarReplAggregatesPass());
|
|
FPM->add(createSimplifyLibCallsPass());
|
|
FPM->add(createInstructionCombiningPass());
|
|
FPM->add(createJumpThreadingPass());
|
|
FPM->add(createCFGSimplificationPass());
|
|
FPM->add(createInstructionCombiningPass());
|
|
FPM->add(createCFGSimplificationPass());
|
|
FPM->add(createReassociatePass());
|
|
FPM->add(createLoopRotatePass());
|
|
FPM->add(createLICMPass());
|
|
FPM->add(createLoopUnswitchPass(false));
|
|
FPM->add(createInstructionCombiningPass());
|
|
FPM->add(createIndVarSimplifyPass());
|
|
FPM->add(createLoopDeletionPass());
|
|
FPM->add(createLoopUnrollPass());
|
|
FPM->add(createInstructionCombiningPass());
|
|
FPM->add(createGVNPass());
|
|
FPM->add(createSCCPPass());
|
|
FPM->add(createInstructionCombiningPass());
|
|
FPM->add(createJumpThreadingPass());
|
|
FPM->add(createDeadStoreEliminationPass());
|
|
FPM->add(createAggressiveDCEPass());
|
|
FPM->add(createCFGSimplificationPass());
|
|
|
|
// Cache the LLVM type signature of an opcode function
|
|
int_type = sizeof(size_t) == 4 ?
|
|
IntegerType::getInt32Ty(Context) :
|
|
IntegerType::getInt64Ty(Context);
|
|
const Type *data_type =
|
|
PointerType::getUnqual(IntegerType::getInt8Ty(Context));
|
|
std::vector<const Type*> args;
|
|
args.push_back(int_type);
|
|
args.push_back(data_type);
|
|
op_type =
|
|
FunctionType::get(Type::getVoidTy(Context), args, false);
|
|
|
|
// Setup a global variable in the LLVM module to represent the bytecode
|
|
// array. Bind it to the actual bytecode array at JIT time.
|
|
const Type *bytecode_type = PointerType::getUnqual(op_type);
|
|
bytecode_array = cast<GlobalValue>(module->
|
|
getOrInsertGlobal("BytecodeArray", bytecode_type));
|
|
EE->addGlobalMapping(bytecode_array, BytecodeArray);
|
|
|
|
// Setup a similar mapping for the global executed flag.
|
|
const IntegerType *flag_type = IntegerType::get(Context, 8);
|
|
executed_flag =
|
|
cast<GlobalValue>(module->getOrInsertGlobal("executed", flag_type));
|
|
EE->addGlobalMapping(executed_flag, &executed);
|
|
|
|
// Cache LLVM declarations for putchar() and getchar().
|
|
const Type *int_type = sizeof(int) == 4 ? IntegerType::getInt32Ty(Context)
|
|
: IntegerType::getInt64Ty(Context);
|
|
putchar_func =
|
|
module->getOrInsertFunction("putchar", int_type, int_type, NULL);
|
|
getchar_func = module->getOrInsertFunction("getchar", int_type, NULL);
|
|
}
|
|
|
|
void BrainFTraceRecorder::compile(BrainFTraceNode* trace) {
|
|
LLVMContext &Context = module->getContext();
|
|
|
|
// Create a new function for the trace we're compiling.
|
|
Function *curr_func = cast<Function>(module->
|
|
getOrInsertFunction("trace_"+utostr(trace->pc), op_type));
|
|
|
|
// Create an entry block, which branches directly to a header block.
|
|
// This is necessary because the entry block cannot be the target of
|
|
// a loop.
|
|
BasicBlock *Entry = BasicBlock::Create(Context, "entry", curr_func);
|
|
Header = BasicBlock::Create(Context, utostr(trace->pc), curr_func);
|
|
|
|
// Mark the array pointer as noalias, and setup compiler state.
|
|
IRBuilder<> builder(Entry);
|
|
Argument *Arg1 = ++curr_func->arg_begin();
|
|
Arg1->addAttr(Attribute::NoAlias);
|
|
DataPtr = Arg1;
|
|
|
|
// Emit code to set the executed flag. This signals to the recorder
|
|
// that the preceding opcode was executed as a part of a compiled trace.
|
|
const IntegerType *flag_type = IntegerType::get(Context, 8);
|
|
ConstantInt *True = ConstantInt::get(flag_type, 1);
|
|
builder.CreateStore(True, executed_flag);
|
|
builder.CreateBr(Header);
|
|
|
|
// Header will be the root of our trace tree. As such, all loop back-edges
|
|
// will be targetting it. Setup a PHI node to merge together incoming values
|
|
// for the current array pointer as we loop.
|
|
builder.SetInsertPoint(Header);
|
|
HeaderPHI = builder.CreatePHI(DataPtr->getType());
|
|
HeaderPHI->addIncoming(DataPtr, Entry);
|
|
DataPtr = HeaderPHI;
|
|
|
|
// Recursively descend the trace tree, emitting code for the opcodes as we go.
|
|
compile_opcode(trace, builder);
|
|
|
|
// Run out optimization suite on our newly generated trace.
|
|
FPM->run(*curr_func);
|
|
|
|
// Compile our trace to machine code, and install function pointer to it
|
|
// into the bytecode array so that it will be executed every time the
|
|
// trace-head PC is reached.
|
|
void *code = EE->getPointerToFunction(curr_func);
|
|
BytecodeArray[trace->pc] =
|
|
(opcode_func_t)(intptr_t)code;
|
|
}
|
|
|
|
/// compile_plus - Emit code for '+'
|
|
void BrainFTraceRecorder::compile_plus(BrainFTraceNode *node,
|
|
IRBuilder<>& builder) {
|
|
Value *CellValue = builder.CreateLoad(DataPtr);
|
|
Constant *One =
|
|
ConstantInt::get(IntegerType::getInt8Ty(Header->getContext()), 1);
|
|
Value *UpdatedValue = builder.CreateAdd(CellValue, One);
|
|
builder.CreateStore(UpdatedValue, DataPtr);
|
|
|
|
if (node->left != (BrainFTraceNode*)~0ULL)
|
|
compile_opcode(node->left, builder);
|
|
else {
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
builder.CreateBr(Header);
|
|
}
|
|
}
|
|
|
|
/// compile_minus - Emit code for '-'
|
|
void BrainFTraceRecorder::compile_minus(BrainFTraceNode *node,
|
|
IRBuilder<>& builder) {
|
|
Value *CellValue = builder.CreateLoad(DataPtr);
|
|
Constant *One =
|
|
ConstantInt::get(IntegerType::getInt8Ty(Header->getContext()), 1);
|
|
Value *UpdatedValue = builder.CreateSub(CellValue, One);
|
|
builder.CreateStore(UpdatedValue, DataPtr);
|
|
|
|
if (node->left != (BrainFTraceNode*)~0ULL)
|
|
compile_opcode(node->left, builder);
|
|
else {
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
builder.CreateBr(Header);
|
|
}
|
|
}
|
|
|
|
/// compile_left - Emit code for '<'
|
|
void BrainFTraceRecorder::compile_left(BrainFTraceNode *node,
|
|
IRBuilder<>& builder) {
|
|
Value *OldPtr = DataPtr;
|
|
DataPtr = builder.CreateConstInBoundsGEP1_32(DataPtr, -1);
|
|
if (node->left != (BrainFTraceNode*)~0ULL)
|
|
compile_opcode(node->left, builder);
|
|
else {
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
builder.CreateBr(Header);
|
|
}
|
|
DataPtr = OldPtr;
|
|
}
|
|
|
|
/// compile_right - Emit code for '>'
|
|
void BrainFTraceRecorder::compile_right(BrainFTraceNode *node,
|
|
IRBuilder<>& builder) {
|
|
Value *OldPtr = DataPtr;
|
|
DataPtr = builder.CreateConstInBoundsGEP1_32(DataPtr, 1);
|
|
if (node->left != (BrainFTraceNode*)~0ULL)
|
|
compile_opcode(node->left, builder);
|
|
else {
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
builder.CreateBr(Header);
|
|
}
|
|
DataPtr = OldPtr;
|
|
}
|
|
|
|
|
|
/// compile_put - Emit code for '.'
|
|
void BrainFTraceRecorder::compile_put(BrainFTraceNode *node,
|
|
IRBuilder<>& builder) {
|
|
Value *Loaded = builder.CreateLoad(DataPtr);
|
|
Value *Print =
|
|
builder.CreateSExt(Loaded, IntegerType::get(Loaded->getContext(), 32));
|
|
builder.CreateCall(putchar_func, Print);
|
|
if (node->left != (BrainFTraceNode*)~0ULL)
|
|
compile_opcode(node->left, builder);
|
|
else {
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
builder.CreateBr(Header);
|
|
}
|
|
}
|
|
|
|
/// compile_get - Emit code for ','
|
|
void BrainFTraceRecorder::compile_get(BrainFTraceNode *node,
|
|
IRBuilder<>& builder) {
|
|
Value *Ret = builder.CreateCall(getchar_func);
|
|
Value *Trunc =
|
|
builder.CreateTrunc(Ret, IntegerType::get(Ret->getContext(), 8));
|
|
builder.CreateStore(Ret, Trunc);
|
|
if (node->left != (BrainFTraceNode*)~0ULL)
|
|
compile_opcode(node->left, builder);
|
|
else {
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
builder.CreateBr(Header);
|
|
}
|
|
}
|
|
|
|
/// compile_if - Emit code for '['
|
|
void BrainFTraceRecorder::compile_if(BrainFTraceNode *node,
|
|
IRBuilder<>& builder) {
|
|
BasicBlock *ZeroChild = 0;
|
|
BasicBlock *NonZeroChild = 0;
|
|
|
|
IRBuilder<> oldBuilder = builder;
|
|
|
|
LLVMContext &Context = Header->getContext();
|
|
|
|
// If both directions of the branch go back to the trace-head, just
|
|
// jump there directly.
|
|
if (node->left == (BrainFTraceNode*)~0ULL &&
|
|
node->right == (BrainFTraceNode*)~0ULL) {
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
builder.CreateBr(Header);
|
|
return;
|
|
}
|
|
|
|
// Otherwise, there are two cases to handle for each direction:
|
|
// ~0ULL - A branch back to the trace head
|
|
// 0 - A branch out of the trace
|
|
// * - A branch to a node we haven't compiled yet.
|
|
// Go ahead and generate code for both targets.
|
|
|
|
if (node->left == (BrainFTraceNode*)~0ULL) {
|
|
NonZeroChild = Header;
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
} else if (node->left == 0) {
|
|
NonZeroChild = BasicBlock::Create(Context,
|
|
"exit_left_"+utostr(node->pc),
|
|
Header->getParent());
|
|
builder.SetInsertPoint(NonZeroChild);
|
|
ConstantInt *NewPc = ConstantInt::get(int_type, node->pc+1);
|
|
Value *BytecodeIndex =
|
|
builder.CreateConstInBoundsGEP1_32(bytecode_array, node->pc+1);
|
|
Value *Target = builder.CreateLoad(BytecodeIndex);
|
|
CallInst *Call =cast<CallInst>(builder.CreateCall2(Target, NewPc, DataPtr));
|
|
Call->setTailCall();
|
|
builder.CreateRetVoid();
|
|
} else {
|
|
NonZeroChild = BasicBlock::Create(Context,
|
|
utostr(node->left->pc),
|
|
Header->getParent());
|
|
builder.SetInsertPoint(NonZeroChild);
|
|
compile_opcode(node->left, builder);
|
|
}
|
|
|
|
if (node->right == (BrainFTraceNode*)~0ULL) {
|
|
ZeroChild = Header;
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
} else if (node->right == 0) {
|
|
ZeroChild = BasicBlock::Create(Context,
|
|
"exit_right_"+utostr(node->pc),
|
|
Header->getParent());
|
|
builder.SetInsertPoint(ZeroChild);
|
|
ConstantInt *NewPc = ConstantInt::get(int_type, JumpMap[node->pc]+1);
|
|
Value *BytecodeIndex =
|
|
builder.CreateConstInBoundsGEP1_32(bytecode_array, JumpMap[node->pc]+1);
|
|
Value *Target = builder.CreateLoad(BytecodeIndex);
|
|
CallInst *Call =cast<CallInst>(builder.CreateCall2(Target, NewPc, DataPtr));
|
|
Call->setTailCall();
|
|
builder.CreateRetVoid();
|
|
} else {
|
|
ZeroChild = BasicBlock::Create(Context,
|
|
utostr(node->right->pc),
|
|
Header->getParent());
|
|
builder.SetInsertPoint(ZeroChild);
|
|
compile_opcode(node->right, builder);
|
|
}
|
|
|
|
// Generate the test and branch to select between the targets.
|
|
Value *Loaded = oldBuilder.CreateLoad(DataPtr);
|
|
Value *Cmp = oldBuilder.CreateICmpEQ(Loaded,
|
|
ConstantInt::get(Loaded->getType(), 0));
|
|
oldBuilder.CreateCondBr(Cmp, ZeroChild, NonZeroChild);
|
|
}
|
|
|
|
/// compile_back - Emit code for ']'
|
|
void BrainFTraceRecorder::compile_back(BrainFTraceNode *node,
|
|
IRBuilder<>& builder) {
|
|
if (node->right != (BrainFTraceNode*)~0ULL)
|
|
compile_opcode(node->right, builder);
|
|
else {
|
|
HeaderPHI->addIncoming(DataPtr, builder.GetInsertBlock());
|
|
builder.CreateBr(Header);
|
|
}
|
|
}
|
|
|
|
/// compile_opcode - Dispatch to a more specific compiler function based
|
|
/// on the opcode of the current node.
|
|
void BrainFTraceRecorder::compile_opcode(BrainFTraceNode *node,
|
|
IRBuilder<>& builder) {
|
|
switch (node->opcode) {
|
|
case '+':
|
|
compile_plus(node, builder);
|
|
break;
|
|
case '-':
|
|
compile_minus(node, builder);
|
|
break;
|
|
case '<':
|
|
compile_left(node, builder);
|
|
break;
|
|
case '>':
|
|
compile_right(node, builder);
|
|
break;
|
|
case '.':
|
|
compile_put(node, builder);
|
|
break;
|
|
case ',':
|
|
compile_get(node, builder);
|
|
break;
|
|
case '[':
|
|
compile_if(node, builder);
|
|
break;
|
|
case ']':
|
|
compile_back(node, builder);
|
|
break;
|
|
}
|
|
}
|