gambas-source-code/gb.jit/src/jit_codegen.cpp
Emil Lenngren e04e8e8672 [GB.JIT]
* BUG: IIf generated incorrect code for the types CSTRING and STRING.


git-svn-id: svn://localhost/gambas/trunk@6599 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2014-10-31 21:57:27 +00:00

7001 lines
252 KiB
C++

/***************************************************************************
jit_codegen.c
gb.jit component
(c) 2012 Emil Lenngren <emil.lenngren [at] gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
***************************************************************************/
#define __JIT_CODEGEN_C
#include <array>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include "jit.h"
#include "jit_runtime.h"
#include "jit_gambas_pass.h"
#define class klass
extern "C" {
#include "gbx_struct.h"
/*#include "gbx_api.h"
#include "gbx_regexp.h"
#include "gbx_math.h"
#define throw _throw
#include "gbx_compare.h"
#undef throw*/
}
#undef class
#ifdef OS_64BITS
#define TARGET_BITS 64
#else
#define TARGET_BITS 32
#endif
#define GOSUB_ON_STACK
#define llvmType(t) llvm::Type::t(llvm_context)
#define pointer_t(type) llvm::PointerType::get((type), 0)
#define charPP llvm::PointerType::get(llvmType(getInt8PtrTy), 0)
#define get_nullptr() llvm::ConstantPointerNull::get(llvmType(getInt8PtrTy))
#define get_voidstring() get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0));
#define LONG_TYPE (TARGET_BITS == 64 ? llvmType(getInt64Ty) : llvmType(getInt32Ty))
#ifdef __CYGWIN__
#define __finite finite
#define __isnan __isnand
#define __isinf __isinfd
#endif
const size_t TYPE_sizeof_memory_tab[16] = { 0, 1, 1, 2, 4, 8, 4, 8, 8, sizeof(void *), sizeof(void *), sizeof(void *), sizeof(VARIANT), 0, 0, 0 };
///DEBUG
void print_type(llvm::Value* v);
static std::set<std::string> mappings;
//static std::map<void*, llvm::GlobalVariable*> variable_mappings;
static llvm::LLVMContext llvm_context;
static llvm::StructType* string_type;
static llvm::StructType* date_type;
static llvm::StructType* function_type;
static llvm::StructType* variant_type;
static llvm::StructType* object_type;
static llvm::StructType* OBJECT_type;
static llvm::StructType* value_type;
static llvm::StructType* value_types[17];
static llvm::StructType* two_longs_type;
static llvm::StructType* gosub_stack_node_type;
static llvm::Module* M;
static llvm::ExecutionEngine* EE = NULL;
static llvm::Function* llvm_function;
static llvm::BasicBlock* entry_block;
static llvm::IRBuilder<>* builder;
static llvm::Value* temp_value;
static llvm::Value* temp_value2;
static llvm::Value* temp_voidptr;
static llvm::Value* temp_int;
static llvm::Value* temp_double;
static llvm::Value* temp_date;
static llvm::Value* temp_2longs;
static llvm::Value* temp_errcontext1;
static llvm::Value* temp_errcontext2;
static llvm::Value* temp_got_error;
static llvm::Value* temp_got_error2;
//static llvm::Value* temp_gosub_stack;
//static llvm::Value* temp_num_gosubs_on_stack;
static llvm::Value* gp;
static llvm::Value* gosub_return_point;
static std::vector<llvm::BasicBlock*> return_points;
static std::vector<llvm::BasicBlock*> addr_blocks; //addr_blocks[i] contains the basic block at code pos i, if the address is taken
struct PendingBranch {
llvm::BasicBlock* insert_point;
llvm::Value* condition; //i1, NULL if unconditional branch
int true_addr;
int false_addr; //Not used if unconditional branch
};
static std::vector<PendingBranch> pending_branches;
struct JumpTablePendingBranch {
llvm::BasicBlock* insert_point;
llvm::Value* condition; //int32
std::vector<int>* destinations;
int default_addr;
};
static std::vector<JumpTablePendingBranch> jump_table_pending_branches;
static std::vector<llvm::BasicBlock*> gosub_continue_points;
static std::vector<llvm::BasicBlock*> gosub_return_points;
static std::vector<llvm::Value*> current_ctrl_types;
static std::vector<std::array<llvm::Value*, 4> > ctrl_values;
static std::vector<llvm::Value*> locals;
static std::vector<llvm::Value*> params;
static llvm::Value* current_op;
static bool in_try = false;
static bool has_tries = false;
static std::vector<llvm::BasicBlock*> try_blocks;
static unsigned short* statement_pc;
static llvm::ConstantInt* getInteger(int bits, uint64_t value){
return llvm::ConstantInt::get(llvm_context, llvm::APInt(bits, value, true));
}
template <typename _FloatType_>
static llvm::ConstantFP* getFloat(_FloatType_ value){
return llvm::ConstantFP::get(llvm_context, llvm::APFloat(value));
}
static llvm::Type* TYPE_llvm(TYPE type){
if (TYPE_is_object(type))
return object_type;
static llvm::Type* const arr[] = {
llvmType(getVoidTy),
llvmType(getInt1Ty),
llvmType(getInt8Ty),
llvmType(getInt16Ty),
llvmType(getInt32Ty),
llvmType(getInt64Ty),
llvmType(getFloatTy),
llvmType(getDoubleTy),
date_type,
string_type,
string_type,
llvmType(getInt8PtrTy),
variant_type,
NULL,
llvmType(getInt8PtrTy),
NULL
};
return arr[type];
}
static llvm::Type* type_from_char(char c){
switch (c){
case 'v': return llvmType(getVoidTy);
case 'b': return llvmType(getInt1Ty);
case 'c': return llvmType(getInt8Ty);
case 'h': return llvmType(getInt16Ty);
case 'i': return llvmType(getInt32Ty);
case 'l': return llvmType(getInt64Ty);
case 'f': return llvmType(getFloatTy);
case 'd': return llvmType(getDoubleTy);
case 'p': return llvmType(getInt8PtrTy);
case 'j': return LONG_TYPE;
}
return 0; //Error
}
static std::vector<llvm::Type*> string_to_type_vector(const char* args){
std::vector<llvm::Type*> llvm_args;
for(int i=0, e=strlen(args); i!=e; i++)
llvm_args.push_back(type_from_char(args[i]));
return llvm_args;
}
static llvm::FunctionType* get_function_type(char ret, const char* args, bool vararg = false){
std::vector<llvm::Type*> llvm_args = string_to_type_vector(args);
return llvm::FunctionType::get(type_from_char(ret), llvm_args, vararg);
}
static llvm::Type* get_value_type(TYPE type){
if (TYPE_is_object(type)) return value_types[T_OBJECT];
return value_types[type];
}
static llvm::BasicBlock* create_bb(const char* name){
return llvm::BasicBlock::Create(llvm_context, name, llvm_function);
}
extern "C" double __powidf2(double, int);
static void llvm_init(){
llvm::InitializeNativeTarget();
//string_type = llvm::StructType::create(llvm_context, string_to_type_vector("jpii"), "String");
date_type = llvm::StructType::create(llvm_context, string_to_type_vector("ii"), "Date");
function_type = llvm::StructType::create(llvm_context, string_to_type_vector("ppcch"), "Function");
variant_type = llvm::StructType::create(llvm_context, string_to_type_vector("jl"), "Variant", true);
object_type = llvm::StructType::create(llvm_context, string_to_type_vector("pp"), "Object");
OBJECT_type = llvm::StructType::create(llvm_context, string_to_type_vector("pj"), "OBJECT");
value_type = llvm::StructType::create(llvm_context, string_to_type_vector("jjjj"), "Value");
two_longs_type = llvm::StructType::create(llvm_context, string_to_type_vector("jj"), "TwoLongs");
#if TARGET_BITS == 64
static const char* const arr[17] = {"jjjj", "ji", "ji", "ji", "ji", "jl", "jf", "jd", "jii", "jpii", NULL, "jp", "jjl", "jppcch", "jp", "j", "ppp"};
#else
static const char* const arr[17] = {"jjjj", "ji", "ji", "ji", "ji", "jil", "jf", "jid", "jii", "jpii", NULL, "jp", "jjl", "jppcch", "jp", "j", "ppp"};
#endif
static const char* const names[17] = {"Void", "Boolean", "Byte", "Short", "Integer", "Long", "Single", "Float", "Date", "String", NULL, "Pointer", "ValueVariant", "ValueFunction", "Class", "Null", "ValueObject"};
for(int i=0; i<=16; i++)
if (arr[i])
value_types[i] = llvm::StructType::create(llvm_context, string_to_type_vector(arr[i]), names[i]);
value_types[T_CSTRING] = value_types[T_STRING];
string_type = value_types[T_STRING];
{
std::vector<llvm::Type*> llvm_args;
//llvm_args.push_back(llvmType(getInt8PtrTy)); //Stores the blockaddress. Is a ushort PC in gbx_stack.h but this should work due to alignment ;)
llvm_args.push_back(llvmType(getInt16Ty)); //Stores an id of the return point, 0 == none
llvm_args.push_back(pointer_t(value_type));
gosub_stack_node_type = llvm::StructType::create(llvm_context, llvm_args, "GosubStackNode");
}
llvm::sys::DynamicLibrary::AddSymbol("__powidf2", (void*)__powidf2);
}
void register_global_symbol(llvm::StringRef name, llvm::GlobalValue* value, void* address){
if (mappings.insert(name).second)
EE->addGlobalMapping(value, address);
}
static llvm::Value* get_global(void* var, llvm::Type* llvm_type = llvmType(getInt8PtrTy)){
/*char buf[32];
sprintf(buf, "v_%p", var);
std::string name = buf;
if (var == &SP)
name = "SP";
else if (var == &BP)
name = "BP";
else if (var == &CP)
name = "CP";
auto it = variable_mappings.insert(std::make_pair(var, (llvm::GlobalVariable*)0));
if (it.second){
llvm::GlobalVariable* v = new llvm::GlobalVariable(*M, llvm_type, false, llvm::GlobalValue::ExternalLinkage, 0, name);
it.first->second = v;
EE->addGlobalMapping(v, var);
return v;
} else {
return it.first->second;
}*/
return builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(var)), pointer_t(llvm_type));
}
#define get_global_function(n, ret, args) get_global_function_real(#n, (void*)n, ret, args)
#define get_global_function_jif(n, ret, args) get_global_function_real(#n, (void*)JIF.F_##n, ret, args)
#define get_global_function_vararg(n, ret, args) get_global_function_real(#n, (void*)n, ret, args, true)
#define get_global_function_jif_vararg(n, ret, args) get_global_function_real(#n, (void*)JIF.F_##n, ret, args, true)
static llvm::Value* get_global_function_real(const char* name, void* func, char ret, const char* args, bool vararg = false){
llvm::FunctionType* ft = get_function_type(ret, args, vararg);
//llvm::sys::DynamicLibrary::AddSymbol
//return get_global(func, ft);
llvm::GlobalValue* val = (llvm::GlobalValue*)M->getOrInsertFunction(name, ft);
register_global_symbol(name, val, func);
return val;
}
static llvm::Value* read_global(void* var, llvm::Type* pt = llvmType(getInt8PtrTy)){
//llvm::Value* addr = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(var)), pointer_t(pt));
llvm::Value* addr = get_global(var, pt);
llvm::Value* v = builder->CreateLoad(addr);
return v;
}
static llvm::Value* create_gep(llvm::Value* addr, int bits1, int val1, int bits2, int val2){
llvm::Value* arr[] = {getInteger(bits1, val1), getInteger(bits2, val2)};
return builder->CreateGEP(addr, arr);
}
static llvm::Value* get_element_addr(llvm::Value* addr, int index){
return create_gep(addr, TARGET_BITS, 0, 32, index);
}
static llvm::Value* load_element(llvm::Value* addr, int index){
return builder->CreateLoad(get_element_addr(addr, index));
}
static void store_element(llvm::Value* addr, int index, llvm::Value* val){
builder->CreateStore(val, get_element_addr(addr, index));
}
static llvm::Value* get_new_struct(llvm::StructType* st){
return llvm::UndefValue::get(st);
}
static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1){
static const unsigned int arr[1] = {0};
return builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr);
}
static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2){
static const unsigned int arr[2][1] = {{0}, {1}};
llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]);
return builder->CreateInsertValue(val, v2, arr[1]);
}
static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3){
static const unsigned int arr[3][1] = {{0}, {1}, {2}};
llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]);
val = builder->CreateInsertValue(val, v2, arr[1]);
return builder->CreateInsertValue(val, v3, arr[2]);
}
static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3, llvm::Value* v4){
static const unsigned int arr[5][1] = {{0}, {1}, {2}, {3}, {4}};
llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]);
val = builder->CreateInsertValue(val, v2, arr[1]);
val = builder->CreateInsertValue(val, v3, arr[2]);
return builder->CreateInsertValue(val, v4, arr[3]);
}
static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3, llvm::Value* v4, llvm::Value* v5){
static const unsigned int arr[5][1] = {{0}, {1}, {2}, {3}, {4}};
llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]);
val = builder->CreateInsertValue(val, v2, arr[1]);
val = builder->CreateInsertValue(val, v3, arr[2]);
val = builder->CreateInsertValue(val, v4, arr[3]);
return builder->CreateInsertValue(val, v5, arr[4]);
}
static llvm::Value* extract_value(llvm::Value* val, int index){
unsigned int arr[] = {index};
return builder->CreateExtractValue(val, arr);
}
static llvm::Value* insert_value(llvm::Value* container, llvm::Value* value, int index){
unsigned int arr[] = {index};
return builder->CreateInsertValue(container, value, arr);
}
static llvm::Value* read_value(llvm::Value* addr, TYPE type){
addr = builder->CreateBitCast(addr, pointer_t(get_value_type(type)));
switch(type){
case T_VOID: return NULL;
case T_BOOLEAN:
return builder->CreateICmpNE(load_element(addr, 1), getInteger(32, 0));
case T_BYTE:
case T_SHORT:
return builder->CreateTrunc(load_element(addr, 1), TYPE_llvm(type));
case T_LONG:
case T_FLOAT:
#if TARGET_BITS == 32
return load_element(addr, 2);
#endif
case T_INTEGER:
case T_SINGLE:
case T_POINTER:
case T_CLASS:
return load_element(addr, 1);
case T_DATE:
return get_new_struct(date_type, load_element(addr, 1), load_element(addr, 2));
case T_STRING:
case T_CSTRING: {
//return builder->CreateLoad(addr);
llvm::Value* t = load_element(addr, 0);
llvm::Value* res = get_new_struct(string_type, t, load_element(addr, 1), load_element(addr, 2), load_element(addr, 3));
/*llvm::BasicBlock *bb1 = create_bb("bb1"), *bb2 = create_bb("bb2");
builder->CreateCondBr(builder->CreateICmpEQ(t, getInteger(TARGET_BITS, 12)), bb1, bb2);
builder->SetInsertPoint(bb1);
builder->CreateCall(get_global_function(abort, 'v', ""));
builder->CreateUnreachable();
builder->SetInsertPoint(bb2);*/
return res;
//FIXME remove above - now outcommented!
/*gen_if_noreturn(builder->CreateICmpEQ(t, getInteger(TARGET_BITS, 12)), [&](){
builder->CreateCall(get_global_function(abort, 'v', ""));
});*/
}
//case T_CSTRING:
// return get_new_struct(string_type, getInteger(1, true), load_element(addr, 1), load_element(addr, 2), load_element(addr, 3));
case T_VARIANT: {
llvm::Value* not_null = get_new_struct(variant_type, load_element(addr, 1), load_element(addr, 2));
llvm::Value* if_null = get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL));
return builder->CreateSelect(builder->CreateICmpNE(load_element(addr, 0), getInteger(TARGET_BITS, T_NULL)), not_null, if_null);
}
case T_FUNCTION:
return get_new_struct(function_type, load_element(addr, 1), load_element(addr, 2), load_element(addr, 3), load_element(addr, 4), load_element(addr, 5));
case T_NULL:
abort();
default: {
//FIXME
//builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"laddar %p\n", llvmType(getInt8Ty)), load_element(addr, 0));
llvm::Value* is_null = load_element(builder->CreateBitCast(addr, pointer_t(value_types[T_NULL])), 0);
is_null = builder->CreateICmpEQ(is_null, getInteger(TARGET_BITS, T_NULL));
//llvm::Value* nullstruct = insert_value(get_new_struct(object_type), get_nullptr(), 1);
llvm::Value* nullstruct = get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr());
return builder->CreateSelect(is_null, nullstruct, get_new_struct(object_type, load_element(addr, 0), load_element(addr, 1)));
}
}
}
static void store_value(llvm::Value* addr, llvm::Value* val, TYPE type, bool store_type = true){
addr = builder->CreateBitCast(addr, pointer_t(get_value_type(type)));
if (store_type && type != T_STRING)
store_element(addr, 0, TYPE_is_object(type) ? builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)type), llvmType(getInt8PtrTy)) : getInteger(TARGET_BITS, type));
switch(type){
case T_BOOLEAN:
case T_SHORT:
store_element(addr, 1, builder->CreateSExt(val, llvmType(getInt32Ty))); break;
case T_BYTE:
store_element(addr, 1, builder->CreateZExt(val, llvmType(getInt32Ty))); break;
case T_LONG:
case T_FLOAT:
#if TARGET_BITS == 32
store_element(addr, 2, val); break;
#endif
case T_INTEGER:
case T_SINGLE:
case T_POINTER:
case T_CLASS:
store_element(addr, 1, val); break;
case T_DATE:
case T_VARIANT:
store_element(addr, 1, extract_value(val, 0));
store_element(addr, 2, extract_value(val, 1));
break;
case T_STRING:
case T_CSTRING:
//builder->CreateStore(val, addr);
store_element(addr, 0, extract_value(val, 0));
store_element(addr, 1, extract_value(val, 1));
store_element(addr, 2, extract_value(val, 2));
store_element(addr, 3, extract_value(val, 3));
/* store_element(addr, 0, builder->CreateSelect(extract_value(val, 0), getInteger(TARGET_BITS, T_CSTRING), getInteger(TARGET_BITS, T_STRING)));
store_element(addr, 1, extract_value(val, 1));
store_element(addr, 2, extract_value(val, 2));
store_element(addr, 3, extract_value(val, 3));
break;
case T_CSTRING:
store_element(addr, 1, extract_value(val, 1));
store_element(addr, 2, extract_value(val, 2));
store_element(addr, 3, extract_value(val, 3));*/
break;
case T_FUNCTION:
store_element(addr, 1, extract_value(val, 0));
store_element(addr, 2, extract_value(val, 1));
store_element(addr, 3, extract_value(val, 2));
store_element(addr, 4, extract_value(val, 3));
store_element(addr, 5, extract_value(val, 4));
break;
case T_VOID:
case T_NULL:
break;
default:
store_element(addr, 0, extract_value(val, 0));
store_element(addr, 1, extract_value(val, 1));
break;
}
}
/*static llvm::Value* array_read(llvm::Value* addr, TYPE type){
switch(type){
case T_VOID:
case T_CSTRING:
case T_NULL:
case T_CLASS:
case T_FUNCTION:
abort();
case T_BOOLEAN: return builder->CreateTrunc(builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt8PtrTy))), llvmType(getInt1Ty));
case T_BYTE: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt8PtrTy)));
case T_SHORT: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt16PtrTy)));
case T_INTEGER: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt32PtrTy)));
case T_LONG: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt64PtrTy)));
case T_SINGLE: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getFloatPtrTy)));
case T_FLOAT: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getDoublePtrTy)));
case T_DATE: return builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(date_type)));
case T_STRING: {
addr = builder->CreateBitCast(addr, charPP);
llvm::Value* len_addr = builder->CreateGEP(addr, getInteger(TARGET_BITS, -4));
len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy));
return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), addr, getInteger(32, 0), builder->CreateLoad(len_addr));
}
case T_POINTER: return builder->CreateLoad(builder->CreateBitCast(addr, charPP));
case T_VARIANT: {
//FIXME can we skip the T_VOID -> T_NULL test?
return builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(variant_type)));
}
default: {
return get_new_struct(object_type, getInteger(TARGET_BITS, type), builder->CreateLoad(builder->CreateBitCast(addr, charPP)));
}
}
}*/
static void store_default(llvm::Value* addr, TYPE type){
switch(type){
case T_VOID: return;
case T_BOOLEAN: builder->CreateStore(getInteger(1, false), addr); return;
case T_BYTE: builder->CreateStore(getInteger(8, 0), addr); return;
case T_SHORT: builder->CreateStore(getInteger(16, 0), addr); return;
case T_INTEGER: builder->CreateStore(getInteger(32, 0), addr); return;
case T_LONG: builder->CreateStore(getInteger(64, 0), addr); return;
case T_SINGLE: builder->CreateStore(getFloat(0.0f), addr); return;
case T_FLOAT: builder->CreateStore(getFloat(0.0), addr); return;
case T_DATE: builder->CreateStore(get_new_struct(date_type, getInteger(32, 0), getInteger(32, 0)), addr); return;
case T_STRING:
case T_CSTRING: builder->CreateStore(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), addr); return;
case T_CLASS: //FIXME?
case T_POINTER: builder->CreateStore(get_nullptr(), addr); return;
case T_VARIANT: builder->CreateStore(get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)/*, undef int64*/), addr); return;
case T_FUNCTION:
case T_NULL: abort();
default:
builder->CreateStore(get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr()), addr); return;
}
}
static llvm::Value* get_default(TYPE type){
switch(type){
case T_VOID: return NULL;
case T_BOOLEAN: return getInteger(1, false);
case T_BYTE: return getInteger(8, 0);
case T_SHORT: return getInteger(16, 0);
case T_INTEGER: return getInteger(32, 0);
case T_LONG: return getInteger(64, 0);
case T_SINGLE: return getFloat(0.0f);
case T_FLOAT: return getFloat(0.0);
case T_DATE: return get_new_struct(date_type, getInteger(32, 0), getInteger(32, 0));
case T_STRING:
case T_CSTRING: return get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0));
case T_CLASS: return NULL;
case T_POINTER: return get_nullptr();
case T_VARIANT: return get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL));
case T_FUNCTION:
case T_NULL: abort();
default:
return get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr());
}
}
template <typename T>
static void gen_if(llvm::Value* cond, T func, const char* if_name="if.then", const char* cont_name="if.cont"){
llvm::BasicBlock* then_block = create_bb(if_name);
llvm::BasicBlock* from_block = builder->GetInsertBlock();
builder->SetInsertPoint(then_block);
func();
llvm::BasicBlock* cont_block = create_bb(cont_name);
builder->CreateBr(cont_block);
builder->SetInsertPoint(from_block);
builder->CreateCondBr(cond, then_block, cont_block);
builder->SetInsertPoint(cont_block);
}
template <typename T>
static llvm::PHINode* gen_if_phi(llvm::Value* else_value, llvm::Value* cond, T func, const char* if_name="if.then", const char* cont_name="if.cont"){
llvm::BasicBlock* then_block = create_bb(if_name);
llvm::BasicBlock* from_block = builder->GetInsertBlock();
builder->SetInsertPoint(then_block);
llvm::Value* ret = func();
llvm::BasicBlock* then_block2 = builder->GetInsertBlock();
llvm::BasicBlock* cont_block = create_bb(cont_name);
builder->CreateBr(cont_block);
builder->SetInsertPoint(from_block);
builder->CreateCondBr(cond, then_block, cont_block);
builder->SetInsertPoint(cont_block);
llvm::PHINode* phi = builder->CreatePHI(ret->getType(), 2);
phi->addIncoming(ret, then_block2);
phi->addIncoming(else_value, from_block);
return phi;
}
template <typename T>
static void gen_if_noreturn(llvm::Value* cond, T func, const char* if_name="if.then", const char* cont_name="if.cont"){
llvm::BasicBlock* then_block = create_bb(if_name);
llvm::BasicBlock* from_block = builder->GetInsertBlock();
builder->SetInsertPoint(then_block);
func();
builder->SetInsertPoint(from_block);
llvm::BasicBlock* cont_block = create_bb(cont_name);
builder->CreateCondBr(cond, then_block, cont_block);
builder->SetInsertPoint(cont_block);
}
template <typename T1, typename T2>
static void gen_if_else(llvm::Value* cond, T1 then_func, T2 else_func, const char* if_name="if.then", const char* else_name="if.else", const char* cont_name="if.cont"){
llvm::BasicBlock* then_block = create_bb(if_name);
llvm::BasicBlock* from_block = builder->GetInsertBlock();
builder->SetInsertPoint(then_block);
then_func();
llvm::BasicBlock* then_block2 = builder->GetInsertBlock();
llvm::BasicBlock* else_block = create_bb(else_name);
builder->SetInsertPoint(else_block);
else_func();
llvm::BasicBlock* else_block2 = builder->GetInsertBlock();
llvm::BasicBlock* cont_block = create_bb(cont_name);
builder->SetInsertPoint(from_block);
builder->CreateCondBr(cond, then_block, else_block);
builder->SetInsertPoint(then_block2);
builder->CreateBr(cont_block);
builder->SetInsertPoint(else_block2);
builder->CreateBr(cont_block);
builder->SetInsertPoint(cont_block);
}
template <typename T1, typename T2>
static llvm::PHINode* gen_if_else_phi(llvm::Value* cond, T1 then_func, T2 else_func, const char* if_name="if.then", const char* else_name="if.else", const char* cont_name="if.cont"){
llvm::BasicBlock* then_block = create_bb(if_name);
llvm::BasicBlock* from_block = builder->GetInsertBlock();
builder->SetInsertPoint(then_block);
llvm::Value* ret1 = then_func();
llvm::BasicBlock* then_block2 = builder->GetInsertBlock();
llvm::BasicBlock* else_block = create_bb(else_name);
builder->SetInsertPoint(else_block);
llvm::Value* ret2 = else_func();
llvm::BasicBlock* else_block2 = builder->GetInsertBlock();
llvm::BasicBlock* cont_block = create_bb(cont_name);
builder->SetInsertPoint(from_block);
builder->CreateCondBr(cond, then_block, else_block);
builder->SetInsertPoint(then_block2);
builder->CreateBr(cont_block);
builder->SetInsertPoint(else_block2);
builder->CreateBr(cont_block);
builder->SetInsertPoint(cont_block);
llvm::PHINode* phi = builder->CreatePHI(ret1->getType(), 2);
phi->addIncoming(ret1, then_block2);
phi->addIncoming(ret2, else_block2);
return phi;
}
template <typename T1>
static void gen_while(llvm::Value* initial_condition, T1 body_func){
llvm::BasicBlock* body_block = create_bb("while_block");
llvm::BasicBlock* from_block = builder->GetInsertBlock();
builder->SetInsertPoint(body_block);
llvm::Value* cont = body_func();
llvm::BasicBlock* cont_block = create_bb("while_cont");
builder->CreateCondBr(cont, body_block, cont_block);
builder->SetInsertPoint(from_block);
builder->CreateCondBr(initial_condition, body_block, cont_block);
builder->SetInsertPoint(cont_block);
}
template <typename T1>
static llvm::Value* gen_and_if(llvm::Value* left, T1 right_func){
llvm::BasicBlock* right_block = create_bb("and_if");
llvm::BasicBlock* from_block = builder->GetInsertBlock();
builder->SetInsertPoint(right_block);
llvm::Value* right = right_func();
llvm::BasicBlock* cont_block = create_bb("and_if_cont");
builder->CreateBr(cont_block);
builder->SetInsertPoint(from_block);
builder->CreateCondBr(left, right_block, cont_block);
builder->SetInsertPoint(cont_block);
llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2);
phi->addIncoming(getInteger(1, false), from_block);
phi->addIncoming(right, right_block);
return phi;
}
static llvm::Value* get_cstring_from_addr(llvm::Value* addr){
llvm::Value* str;
llvm::BasicBlock* B1 = builder->GetInsertBlock();
llvm::BasicBlock* B2;
gen_if(builder->CreateICmpNE(addr, get_nullptr()), [&](){
llvm::Value* len = builder->CreateCall(get_global_function(strlen, 'j', "p"), addr);
if (TARGET_BITS == 64)
len = builder->CreateTrunc(len, llvmType(getInt32Ty));
str = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), addr, getInteger(32, 0), len);
B2 = builder->GetInsertBlock();
}, "cstring_strlen", "cstring_null_or_done_strlen");
llvm::PHINode* phi = builder->CreatePHI(string_type, 2);
phi->addIncoming(get_default(T_CSTRING), B1);
phi->addIncoming(str, B2);
return phi;
}
static llvm::Value* to_target_int(llvm::Value* integer32){
if (TARGET_BITS == 64)
return builder->CreateZExt(integer32, llvmType(getInt64Ty));
return integer32;
}
static void init_locals(){
int n_local = FP->n_local;
int n_ctrl = FP->n_ctrl;
int n_param = FP->n_param;
locals.resize(n_local + n_ctrl);
int i;
for(i=0; i<n_local; i++){
locals[i] = builder->CreateAlloca(TYPE_llvm(ctype_to_type(&FP->local[i].type)));
builder->CreateStore(get_default(ctype_to_type(&FP->local[i].type)), locals[i]);
//builder->CreateStore(read_value(builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE))), ctype_to_type(&FP->local[i].type)), locals[i]);
}
/*for(i; i < n_local + n_ctrl; i++){
TYPE type = get_ctrl_type(i);
if (type != T_VOID){
locals[i] = builder->CreateAlloca(TYPE_llvm(type));
store_default(locals[i], type);
}
}*/
ctrl_values.resize(n_ctrl);
current_ctrl_types.resize(n_ctrl);
for(; i < n_local + n_ctrl; i++){
set_ctrl_type(T_VOID, i);
locals[i] = NULL;
current_ctrl_types[i - n_local] = builder->CreateAlloca(llvmType(getInt32Ty));
builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - n_local]);
ctrl_values[i - n_local][1] = builder->CreateAlloca(string_type);
ctrl_values[i - n_local][2] = builder->CreateAlloca(object_type);
ctrl_values[i - n_local][3] = builder->CreateAlloca(variant_type);
}
params.resize(n_param);
for(i=0; i<n_param; i++){
params[i] = builder->CreateAlloca(TYPE_llvm(FP->param[i].type));
builder->CreateStore(read_value(builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, (i-n_param)*sizeof(VALUE))), FP->param[i].type), params[i]);
}
}
static llvm::Value* release_ctrl(int i);
static void finish_gosub_returns(){
llvm::BasicBlock* bb = builder->GetInsertBlock();
for(size_t i=0, e=gosub_return_points.size(); i!=e; i++){
builder->SetInsertPoint(gosub_return_points[i]);
/*llvm::Value* index = builder->CreateSub(builder->CreateLoad(temp_num_gosubs_on_stack), getInteger(32, 1));
builder->CreateStore(index, temp_num_gosubs_on_stack);
llvm::Value* indices[2] = {getInteger(TARGET_BITS, 0), to_target_int(index)};
llvm::Value* addr = builder->CreateGEP(temp_gosub_stack, indices);*/
//Cleanup current ctrl values
for(int i=0; i<FP->n_ctrl; i++){
llvm::Value* old_type = release_ctrl(i + FP->n_local);
builder->CreateStore(getInteger(32, 0), current_ctrl_types[i]);
gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, (i + FP->n_local)*sizeof(VALUE)));
store_value(stack_addr, NULL, T_VOID);
}, "old_ctrl_needs_to_be_cleaned1");
}
//And return
/*llvm::Value* addr = builder->CreateLoad(gosub_return_point);
llvm::IndirectBrInst* indbrinst = builder->CreateIndirectBr(addr, gosub_continue_points.size());
for(size_t j=0, e=gosub_continue_points.size(); j!=e; j++){
indbrinst->addDestination(gosub_continue_points[j]);
}*/
llvm::Value* return_id = builder->CreateLoad(gosub_return_point);
llvm::SwitchInst* switchinst = builder->CreateSwitch(return_id, gosub_continue_points[0], gosub_continue_points.size()-1);
for(size_t j=1, e=gosub_continue_points.size(); j!=e; j++){
switchinst->addCase(getInteger(16, j+1), gosub_continue_points[j]);
}
}
gosub_return_points.clear();
builder->SetInsertPoint(bb);
}
static void create_return(){
llvm::BasicBlock* BB = create_bb("return");
builder->CreateBr(BB);
for(size_t i=0, e=return_points.size(); i!=e; i++){
builder->SetInsertPoint(return_points[i]);
builder->CreateBr(BB);
}
return_points.clear();
builder->SetInsertPoint(BB);
if (func_byref_mask){
int nparam = FP->n_param;
for(int i=0; i<nparam; i++) if (1ULL<<i & func_byref_mask){
TYPE type = FP->param[i].type;
if (type != T_STRING && !TYPE_is_object(type)){
llvm::Value* v = builder->CreateLoad(params[i]);
llvm::Value* stack_addr = builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, (i-nparam)*sizeof(VALUE)));
store_value(stack_addr, v, type);
}
}
}
//static int counter = 0;
//builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"leave %d\n", llvmType(getInt8Ty)), getInteger(32, counter++));
builder->CreateCall(get_global_function_jif(EXEC_leave_keep, 'v', ""));
//builder->CreateCall(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"leave done\n", llvmType(getInt8Ty)));
builder->CreateRetVoid();
}
static llvm::Value* read_sp();
static void codegen_statements(){
addr_blocks.resize(all_statements.back()->addr + 1);
for(size_t i=0, e=all_statements.size(); i!=e; i++){
if (all_statements[i]->address_taken){
llvm::BasicBlock* block = create_bb("block");
builder->CreateBr(block);
builder->SetInsertPoint(block);
addr_blocks[all_statements[i]->addr] = block;
}
statement_pc = FP->code + all_statements[i]->addr;
/*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)"before: %p\n", llvmType(getInt8Ty)), read_sp());*/
all_statements[i]->expr->codegen();
/*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)"after: %p\n", llvmType(getInt8Ty)), read_sp());*/
}
}
static void insert_pending_branches(){
for(size_t i=0, e=pending_branches.size(); i!=e; i++){
PendingBranch& p = pending_branches[i];
builder->SetInsertPoint(p.insert_point);
if (p.condition){
builder->CreateCondBr(p.condition, addr_blocks[p.true_addr], addr_blocks[p.false_addr]);
} else {
builder->CreateBr(addr_blocks[p.true_addr]);
}
}
pending_branches.clear();
for(size_t i=0, e=jump_table_pending_branches.size(); i!=e; i++){
JumpTablePendingBranch& p = jump_table_pending_branches[i];
builder->SetInsertPoint(p.insert_point);
llvm::SwitchInst* sw = builder->CreateSwitch(p.condition, addr_blocks[p.default_addr], p.destinations->size());
for(size_t j=0, ee=p.destinations->size(); j!=ee; j++)
sw->addCase(getInteger(32, j), addr_blocks[(*p.destinations)[j]]);
}
jump_table_pending_branches.clear();
}
static void store_pc(unsigned short* pc){
builder->CreateStore(getInteger(TARGET_BITS, (int64_t)(intptr_t)pc), get_global((void*)&PC, LONG_TYPE));
}
static void create_throw(int code){
if (FP->debug)
store_pc(statement_pc);
builder->CreateCall(get_global_function_jif_vararg(THROW, 'v', "i"), getInteger(32, code));
builder->CreateUnreachable();
}
static void create_throw(int code, const char* a, const char* b){
if (FP->debug)
store_pc(statement_pc);
builder->CreateCall3(get_global_function_jif_vararg(THROW, 'v', "i"), getInteger(32, code), get_global((void*)a, llvmType(getInt8Ty)), get_global((void*)b, llvmType(getInt8Ty)));
builder->CreateUnreachable();
}
static void create_throw(llvm::Value* code){
if (FP->debug)
store_pc(statement_pc);
builder->CreateCall(get_global_function_jif_vararg(THROW, 'v', "i"), code);
builder->CreateUnreachable();
}
static llvm::Value* get_value_on_top_addr(){
//llvm::Value* sp = builder->CreateBitCast(read_global(&SP), pointer_t(value_type));
llvm::Value* sp = read_global(&SP, pointer_t(value_type));
return builder->CreateGEP(sp, getInteger(TARGET_BITS, -1));
}
static llvm::Value* get_top(TYPE type){
return read_value(get_value_on_top_addr(), type);
}
static llvm::Value* read_sp(){
return read_global(&SP, pointer_t(value_type));
}
static void store_sp(llvm::Value* sp){
llvm::Value* sp_addr = get_global(&SP, pointer_t(value_type));
builder->CreateStore(sp, sp_addr);
}
static void c_SP(int diff){
if (diff == 0) return;
llvm::Value* sp_addr = get_global(&SP, pointer_t(value_type));
llvm::Value* sp = builder->CreateLoad(sp_addr);
sp = builder->CreateGEP(sp, getInteger(TARGET_BITS, diff));
builder->CreateStore(sp, sp_addr);
}
static void push_value(llvm::Value* val, TYPE type){
//c_SP(1);
//store_value(get_value_on_top_addr(), val, type);
llvm::Value* top = get_global(&SP, pointer_t(value_type));
llvm::Value* addr = builder->CreateLoad(top);
store_value(addr, val, type);
builder->CreateStore(builder->CreateGEP(addr, getInteger(TARGET_BITS, 1)), top);
}
static void set_top_value(llvm::Value* val, TYPE type, bool store_type = true){
store_value(get_value_on_top_addr(), val, type, store_type);
}
static llvm::Value* ret_top_stack(TYPE type, bool save){
llvm::Value* val = read_value(get_value_on_top_addr(), type);
if (!save)
c_SP(-1);
return val;
}
static void unref_string_no_nullcheck(llvm::Value* ptr){
llvm::Value* str_ptr = builder->CreateBitCast(ptr, pointer_t(llvmType(getInt32Ty)));
llvm::Value* ref_addr = builder->CreateGEP(str_ptr, getInteger(TARGET_BITS, -2));
llvm::Value* ref = builder->CreateLoad(ref_addr);
/*gen_if_noreturn(builder->CreateICmpSLE(ref, getInteger(32, 0)), [&](){
builder->CreateCall(get_global_function(abort, 'v', ""));
builder->CreateUnreachable();
});
gen_if_noreturn(builder->CreateICmpUGE(ref, getInteger(32, 10000)), [&](){
builder->CreateCall(get_global_function(abort, 'v', ""));
builder->CreateUnreachable();
});*/
ref = builder->CreateSub(ref, getInteger(32, 1));
builder->CreateStore(ref, ref_addr);
llvm::Value* slt = builder->CreateICmpSLT(ref, getInteger(32, 1));
if (llvm::Instruction* inst = llvm::dyn_cast<llvm::Instruction>(slt)){
llvm::Value* arr[1] = {getInteger(32, 1)};
inst->setMetadata("unref_slt", llvm::MDNode::get(llvm_context, arr));
}
gen_if(slt, [&](){
llvm::Value* free_func = get_global_function_jif(STRING_free_real, 'v', "p");
builder->CreateCall(free_func, ptr);
}, "release_str", "release_done");
}
static void unref_string(llvm::Value* ptr){
gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){
unref_string_no_nullcheck(ptr);
}, "str_not_null", "unref_done");
}
static void unref_object_no_nullcheck(llvm::Value* ptr){
/*static int counter = 0;
builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)"unref #: %d, %p\n", llvmType(getInt8Ty)), getInteger(32, counter++), ptr);*/
llvm::Value* object_ptr = builder->CreateBitCast(ptr, pointer_t(OBJECT_type));
llvm::Value* ref_addr = get_element_addr(object_ptr, 1);
llvm::Value* ref = builder->CreateLoad(ref_addr);
ref = builder->CreateSub(ref, getInteger(TARGET_BITS, 1));
builder->CreateStore(ref, ref_addr);
/*builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)"%p %ld -\n", llvmType(getInt8Ty)), ptr, ref);*/
llvm::Value* slt = builder->CreateICmpSLT(ref, getInteger(TARGET_BITS, 1));
if (llvm::Instruction* inst = llvm::dyn_cast<llvm::Instruction>(slt)){
llvm::Value* arr[1] = {getInteger(32, 1)};
inst->setMetadata("unref_slt", llvm::MDNode::get(llvm_context, arr));
}
gen_if(slt, [&](){
llvm::Value* free_func = get_global_function_jif(CLASS_free, 'v', "p");
builder->CreateCall(free_func, ptr);
}, "release_obj", "release_done");
}
static void unref_object(llvm::Value* ptr){
gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){
unref_object_no_nullcheck(ptr);
}, "obj_not_null", "unref_done");
}
static void codegen_printf(const char* str1, int tal){
/*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)str1, llvmType(getInt8Ty)), getInteger(32, tal));*/
}
static void codegen_printf(const char* str1){
/*builder->CreateCall(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)str1, llvmType(getInt8Ty)));*/
}
static void codegen_printf(const char* str1, llvm::Value* intval){
/*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)str1, llvmType(getInt8Ty)), intval);*/
}
static void borrow_string_no_nullcheck(llvm::Value* ptr){
llvm::Value* str_ptr = builder->CreateBitCast(ptr, pointer_t(llvmType(getInt32Ty)));
llvm::Value* ref_addr = builder->CreateGEP(str_ptr, getInteger(TARGET_BITS, -2));
llvm::Value* ref = builder->CreateLoad(ref_addr);
/*gen_if_noreturn(builder->CreateICmpSLE(ref, getInteger(32, 0)), [&](){
builder->CreateCall(get_global_function(abort, 'v', ""));
builder->CreateUnreachable();
});
gen_if_noreturn(builder->CreateICmpUGT(ref, getInteger(32, 10000)), [&](){
builder->CreateCall(get_global_function(abort, 'v', ""));
builder->CreateUnreachable();
});
builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)"%p %d\n", llvmType(getInt8Ty)), ptr, ref);*/
ref = builder->CreateAdd(ref, getInteger(32, 1));
builder->CreateStore(ref, ref_addr);
}
static void borrow_string(llvm::Value* ptr){
gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){
borrow_string_no_nullcheck(ptr);
}, "str_not_null", "string_borrow_done");
}
static void borrow_object_no_nullcheck(llvm::Value* ptr){
llvm::Value* object_ptr = builder->CreateBitCast(ptr, pointer_t(OBJECT_type));
llvm::Value* ref_addr = get_element_addr(object_ptr, 1);
llvm::Value* ref = builder->CreateLoad(ref_addr);
ref = builder->CreateAdd(ref, getInteger(TARGET_BITS, 1));
builder->CreateStore(ref, ref_addr);
/*builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)"%p %ld +\n", llvmType(getInt8Ty)), ptr, ref);*/
}
static void borrow_object(llvm::Value* ptr){
gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){
borrow_object_no_nullcheck(ptr);
}, "obj_not_null", "borrow_object_done");
}
static void borrow_variant(llvm::Value* val){
builder->CreateCall2(
get_global_function(JR_borrow_variant, 'v', "jl"),
extract_value(val, 0),
extract_value(val, 1));
}
static void release(llvm::Value* val, TYPE type){
if (TYPE_is_object(type))
unref_object(extract_value(val, 1));
else if (type == T_STRING){
gen_if(builder->CreateICmpEQ(extract_value(val, 0), getInteger(TARGET_BITS, T_STRING)), [&](){
unref_string(extract_value(val, 1));
}, "release_T_STRING", "str_release_done");
} else if (TYPE_is_variant(type))
builder->CreateCall2(
get_global_function(JR_release_variant, 'v', "jl"),
extract_value(val, 0),
extract_value(val, 1));
}
static void borrow(llvm::Value* val, TYPE type){
if (TYPE_is_object(type))
borrow_object(extract_value(val, 1));
else if (type == T_STRING){
gen_if(builder->CreateICmpEQ(extract_value(val, 0), getInteger(TARGET_BITS, T_STRING)), [&](){
borrow_string(extract_value(val, 1));
}, "borrow_T_STRING", "str_borrow_done");
} else if (TYPE_is_variant(type))
borrow_variant(val);
}
static void borrow_top(TYPE type){
borrow(get_top(type), type);
}
static void release_top(Expression* expr){
if (TYPE_is_object(expr->type)){
unref_object(extract_value(expr->codegen_get_value(), 1));
} else if (expr->type == T_STRING){
release(expr->codegen_get_value(), T_STRING);
} else if (TYPE_is_variant(expr->type)){
expr->codegen();
llvm::Value* free_variant_func = get_global_function_jif(EXEC_release, 'v', "jp");
builder->CreateCall2(free_variant_func, getInteger(TARGET_BITS, T_VARIANT), get_value_on_top_addr());
} else
expr->codegen();
c_SP(-1);
}
static void release_val(Expression* expr){
if (TYPE_is_object(expr->type)){
unref_object(extract_value(expr->codegen_get_value(), 1));
} else if (TYPE_is_string(expr->type)){
release(expr->codegen_get_value(), T_STRING);
} else if (TYPE_is_variant(expr->type)){
expr->on_stack = true;
expr->codegen();
llvm::Value* free_variant_func = get_global_function_jif(EXEC_release, 'v', "jp");
builder->CreateCall2(free_variant_func, getInteger(TARGET_BITS, T_VARIANT), get_value_on_top_addr());
c_SP(-1);
} else
expr->codegen();
}
///Convert a String to a pointer that can be put in an array or a Variant.
///If 'val' is a guaranteed to be T_CSTRING, set type to T_CSTRING, otherwise set it to T_STRING
static llvm::Value* string_for_array_or_variant(llvm::Value* val, TYPE type){
llvm::Value* len = extract_value(val, 3);
return gen_if_phi(get_nullptr(), builder->CreateICmpNE(len, getInteger(32, 0)), [&](){
llvm::Value* ptr = extract_value(val, 1);
llvm::Value* offset = extract_value(val, 2);
if (type == T_STRING){
llvm::Value* is_t_string = builder->CreateICmpEQ(extract_value(val, 0), getInteger(TARGET_BITS, T_STRING));
llvm::Value* test1 = gen_and_if(builder->CreateAnd(
is_t_string,
builder->CreateICmpEQ(offset, getInteger(32, 0))), [&](){
llvm::Value* len_addr = builder->CreateGEP(ptr, getInteger(TARGET_BITS, -4));
len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy));
return builder->CreateICmpEQ(builder->CreateLoad(len_addr), len);
});
return (llvm::Value*)gen_if_phi(ptr, builder->CreateXor(test1, getInteger(1, 1)), [&](){
llvm::Value* newstr = builder->CreateCall2(get_global_function_jif(STRING_new, 'p', "pi"), builder->CreateGEP(ptr, to_target_int(offset)), len);
newstr = builder->CreateCall(get_global_function_jif(STRING_free_later, 'p', "p"), newstr);
gen_if(is_t_string, [&](){
unref_string_no_nullcheck(ptr);
});
borrow_string_no_nullcheck(newstr);
return newstr;
});
} else {
llvm::Value* newstr = builder->CreateCall2(get_global_function_jif(STRING_new, 'p', "pi"), builder->CreateGEP(ptr, to_target_int(offset)), len);
newstr = builder->CreateCall(get_global_function_jif(STRING_free_later, 'p', "p"), newstr);
borrow_string_no_nullcheck(newstr);
return newstr;
}
});
}
///Transfers the ownership from val to the variable in memory
///The old data in memory is not released
static void variable_write(llvm::Value* addr, llvm::Value* val, TYPE type){
if (type != T_BOOLEAN && type != T_STRING && type != T_CSTRING && !TYPE_is_object(type))
addr = builder->CreateBitCast(addr, pointer_t(TYPE_llvm(type)));
/*if (type == T_BOOLEAN)
char* */
switch(type){
case T_VOID:
case T_NULL:
case T_CLASS:
case T_FUNCTION:
abort();
case T_BOOLEAN: builder->CreateStore(builder->CreateSExt(val, llvmType(getInt8Ty)), addr); break;
case T_BYTE:
case T_SHORT:
case T_INTEGER:
case T_LONG:
case T_SINGLE:
case T_FLOAT:
case T_DATE:
case T_POINTER:
case T_VARIANT:
builder->CreateStore(val, addr); break;
case T_STRING:
case T_CSTRING: {
addr = builder->CreateBitCast(addr, charPP);
builder->CreateStore(string_for_array_or_variant(val, type), addr);
break;
}
default: {
builder->CreateStore(extract_value(val, 1), builder->CreateBitCast(addr, charPP));
break;
}
}
}
///Borrows the data
static llvm::Value* array_read(llvm::Value* addr, TYPE type){
if (type != T_BOOLEAN && type != T_STRING && type != T_CSTRING && !TYPE_is_object(type))
addr = builder->CreateBitCast(addr, pointer_t(TYPE_llvm(type)));
switch(type){
case T_VOID:
case T_CSTRING:
case T_NULL:
case T_CLASS:
case T_FUNCTION:
abort();
case T_BOOLEAN: return builder->CreateTrunc(builder->CreateLoad(addr), llvmType(getInt1Ty));
case T_BYTE:
case T_SHORT:
case T_INTEGER:
case T_LONG:
case T_SINGLE:
case T_FLOAT:
case T_DATE:
case T_POINTER:
return builder->CreateLoad(addr);
case T_VARIANT: {
llvm::Value* ret = builder->CreateLoad(addr);
borrow_variant(ret);
return ret;
}
case T_STRING: {
llvm::Value* ptr = builder->CreateLoad(builder->CreateBitCast(addr, charPP));
return gen_if_phi(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)),
builder->CreateICmpNE(ptr, get_nullptr()), [&](){
llvm::Value* len_addr = builder->CreateGEP(ptr, getInteger(TARGET_BITS, -4));
llvm::Value* len = builder->CreateLoad(builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy)));
borrow_string_no_nullcheck(ptr);
return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), ptr, getInteger(32, 0), len);
});
}
default: {
llvm::Value* ret = builder->CreateLoad(builder->CreateBitCast(addr, charPP));
borrow_object(ret);
return get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret);
}
}
}
void DropExpression::codegen(){
/*if (expr->on_stack){
release_top(expr);
} else {
release_val(expr);
}*/
if (CallExpression* ce = dyn_cast<CallExpression>(expr)){
if (ce->variant_call){
ce->codegen_on_stack();
llvm::Value* ret = get_value_on_top_addr();
gen_if(builder->CreateICmpNE(load_element(ret, 0), getInteger(TARGET_BITS, 0)), [&](){
release(ret_top_stack(T_VARIANT, true), T_VARIANT);
});
c_SP(-1);
return;
}
}
release(expr->codegen_get_value(), expr->type);
if (expr->on_stack)
c_SP(-1);
}
llvm::Value* PushCStringExpression::codegen_get_value(){
llvm::Value* ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING),
builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)addr), llvmType(getInt8PtrTy)),
getInteger(32, start), getInteger(32, len));
if (on_stack)
push_value(ret, type);
return ret;
}
void PushIntegerExpression::codegen(){
if (on_stack) codegen_on_stack();
}
llvm::Value* PushIntegerExpression::codegen_get_value(){
llvm::Value* val = getInteger(bits, i);
if (on_stack) codegen_on_stack();
return val;
}
void PushIntegerExpression::codegen_on_stack(){
push_value(getInteger(bits, i), type);
}
llvm::Value* PushFloatExpression::codegen_get_value(){
llvm::Value* ret = type == T_SINGLE ? getFloat((float)val) : getFloat(val);
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* PushNullPointerExpression::codegen_get_value(){
llvm::Value* ret = get_nullptr();
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* PushVoidDateExpression::codegen_get_value(){
llvm::Value* ret = get_new_struct(date_type, getInteger(32, 0), getInteger(32, 0));
if (on_stack)
push_value(ret, T_DATE);
return ret;
}
llvm::Value* PushNullExpression::codegen_get_value(){
if (on_stack)
push_value(NULL, T_NULL);
return NULL;
}
llvm::Value* PushVoidExpression::codegen_get_value(){
if (on_stack)
push_value(NULL, T_VOID);
return NULL;
}
llvm::Value* PushLastExpression::codegen_get_value(){
llvm::Value* obj = read_global((void*)&EVENT_Last);
borrow_object(obj);
llvm::Value* ret = get_new_struct(object_type,
builder->CreateIntToPtr(getInteger(TARGET_BITS, 16), llvmType(getInt8PtrTy)), obj);
if (on_stack)
push_value(ret, T_OBJECT);
return ret;
}
static llvm::Value* read_variable(TYPE type, char* addr){
llvm::Value* ret;
if (type == T_BOOLEAN)
ret = builder->CreateTrunc(read_global(addr, llvmType(getInt8Ty)), llvmType(getInt1Ty));
else if (type < T_STRING || type == T_POINTER)
ret = read_global(addr, TYPE_llvm(type));
else if (type == T_STRING){
llvm::Value* ad = read_global(addr);
ret = gen_if_phi(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), builder->CreateICmpNE(ad, get_nullptr()), [&](){
borrow_string_no_nullcheck(ad);
llvm::Value* len_addr = builder->CreateGEP(ad, getInteger(TARGET_BITS, -4));
len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy));
llvm::Value* len = builder->CreateLoad(len_addr);
return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), ad, getInteger(32, 0), len);
});
} else if (type == T_CSTRING){
llvm::Value* ad = read_global(addr);
ret = get_cstring_from_addr(ad);
} else if (TYPE_is_object(type)){
llvm::Value* obj = read_global(addr);
ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), obj);
borrow_object(obj);
} else if (type == T_VARIANT){ //FIXME
ret = read_global(addr, variant_type);
ret = gen_if_else_phi(builder->CreateICmpEQ(extract_value(ret, 0), getInteger(TARGET_BITS, T_VOID)), [&](){
return get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)/*, extract_value(ret, 1)*/);
}, [&](){
borrow_variant(ret);
return ret;
}, "Variant_T_VOID", "Variant_not_T_VOID", "Variant_T_VOID_done");
} else abort();
return ret;
}
static llvm::Value* codegen_tc_array(CLASS* klass, llvm::Value* ref, int array_load_index, llvm::Value* start_addr, TYPE type){
llvm::Value* obj = builder->CreateCall4(get_global_function_jif(CARRAY_create_static, 'p', "pppp"),
get_global((void*)klass, llvmType(getInt8Ty)),
ref,
get_global(klass->load->array[array_load_index], llvmType(getInt8Ty)),
start_addr);
borrow_object_no_nullcheck(obj);
return get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), obj);
}
llvm::Value* ReadVariableExpression::codegen_get_value(){
llvm::Value* ret;
if (ctype->id == TC_ARRAY){
ret = codegen_tc_array(CP, get_global((void*)CP, llvmType(getInt8Ty)), ctype->value, get_global((void*)addr, llvmType(getInt8Ty)), type);
} else if (ctype->id == TC_STRUCT){
ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"),
get_global((void*)klass, llvmType(getInt8Ty)),
builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)),
get_global((void*)addr, llvmType(getInt8Ty)));
borrow_object_no_nullcheck(ret);
ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret);
} else {
ret = read_variable(type, addr);
}
if (on_stack)
push_value(ret, type);
return ret;
}
//FIXME this is almost same as array_read, only difference is in variant
static llvm::Value* read_variable(TYPE type, llvm::Value* addr){
llvm::Value* ret;
if (type == T_BOOLEAN)
ret = builder->CreateTrunc(builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt8PtrTy))), llvmType(getInt1Ty));
else if (type < T_STRING || type == T_POINTER)
ret = builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(TYPE_llvm(type))));
else if (type == T_STRING){
llvm::Value* ad = builder->CreateLoad(builder->CreateBitCast(addr, charPP));
ret = gen_if_phi(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), builder->CreateICmpNE(ad, get_nullptr()), [&](){
borrow_string_no_nullcheck(ad);
llvm::Value* len_addr = builder->CreateGEP(ad, getInteger(TARGET_BITS, -4));
len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy));
llvm::Value* len = builder->CreateLoad(len_addr);
return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), ad, getInteger(32, 0), len);
});
} else if (type == T_CSTRING){
llvm::Value* ad = builder->CreateLoad(builder->CreateBitCast(addr, charPP));
ret = get_cstring_from_addr(ad);
} else if (TYPE_is_object(type)){
llvm::Value* nobj = builder->CreateLoad(builder->CreateBitCast(addr, charPP));
ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), nobj);
borrow_object(nobj);
} else if (type == T_VARIANT){
ret = builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(variant_type)));
ret = gen_if_else_phi(builder->CreateICmpEQ(extract_value(ret, 0), getInteger(TARGET_BITS, T_VOID)), [&](){
return get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)/*, extract_value(ret, 1)*/);
}, [&](){
borrow_variant(ret);
return ret;
}, "Variant_T_VOID", "Variant_not_T_VOID", "Variant_T_VOID_done");
} else abort();
return ret;
}
///Reading an instance variable
static llvm::Value* read_variable_offset(TYPE type, llvm::Value* obj, llvm::Value* offset){
return read_variable(type, builder->CreateGEP(obj, offset));
}
static void release_variable(TYPE type, llvm::Value* addr){
if (type == T_STRING){
unref_string(builder->CreateLoad(builder->CreateBitCast(addr, charPP)));
} else if (TYPE_is_object(type)){
unref_object(builder->CreateLoad(builder->CreateBitCast(addr, charPP)));
} else if (type == T_VARIANT){
release(builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(variant_type))), T_VARIANT);
/*builder->CreateCall2(get_global_function(JR_release_variant, 'v', "jl"),
builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(LONG_TYPE))),
builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(addr, llvmType(getInt64PtrTy)), getInteger(TARGET_BITS, 1)))
);*/
}
}
static void write_variable_offset(TYPE type, llvm::Value* obj, int offset, llvm::Value* val){
variable_write(builder->CreateGEP(obj, getInteger(TARGET_BITS, offset)), val, type);
}
static void make_nullcheck(llvm::Value* addr){
gen_if_noreturn(builder->CreateICmpEQ(addr, get_nullptr()), [&](){
create_throw(E_NULL);
}, "is_null", "not_null");
}
template <typename T>
static void make_nullcheck(llvm::Value* addr, T func_if_error){
gen_if_noreturn(builder->CreateICmpEQ(addr, get_nullptr()), [&](){
func_if_error();
create_throw(E_NULL);
}, "is_null", "not_null");
}
static void make_double_nullcheck(llvm::Value* value){
//FIXME: I think it is better to maybe make sure that an object on the stack actually
//is of type object (nullpointer or not), instead of T_NULL.
llvm::Value* valtype = builder->CreatePtrToInt(extract_value(value, 0), LONG_TYPE);
gen_if_noreturn(builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_NULL)), [&](){
create_throw(E_NULL);
});
llvm::Value* object_ptr = extract_value(value, 1);
make_nullcheck(object_ptr);
}
template <typename T>
static void make_double_nullcheck(llvm::Value* value, T func_if_error){
//FIXME: I think it is better to maybe make sure that an object on the stack actually
//is of type object (nullpointer or not), instead of T_NULL.
llvm::Value* valtype = builder->CreatePtrToInt(extract_value(value, 0), LONG_TYPE);
gen_if_noreturn(builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_NULL)), [&](){
func_if_error();
create_throw(E_NULL);
});
llvm::Value* object_ptr = extract_value(value, 1);
make_nullcheck(object_ptr, func_if_error);
}
static llvm::Value* get_class_desc_entry(llvm::Value* object_ptr, int index){
llvm::Value* class_ptr = load_element(builder->CreateBitCast(object_ptr, pointer_t(OBJECT_type)), 0);
llvm::Value* vtable_ptr = builder->CreateGEP(class_ptr, getInteger(TARGET_BITS, offsetof(CLASS, table)));
llvm::Value* vtable = builder->CreateLoad(builder->CreateBitCast(vtable_ptr, charPP));
llvm::Value* class_desc_ptr = builder->CreateGEP(vtable, getInteger(TARGET_BITS, index*sizeof(CLASS_DESC_SYMBOL)+offsetof(CLASS_DESC_SYMBOL, desc)));
llvm::Value* class_desc = builder->CreateLoad(builder->CreateBitCast(class_desc_ptr, charPP));
return class_desc;
}
static void create_check(CLASS* klass, llvm::Value* effective_class, llvm::Value* object){
if (klass->must_check){
//Assume klass->must_check only needs to be checked here, and not in the generated code
int offset_check = offsetof(CLASS, check)/sizeof(void*); //From CLASS structure in gbx_class.h
llvm::Value* check_func = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, charPP), getInteger(TARGET_BITS, offset_check)));
check_func = builder->CreateBitCast(check_func, pointer_t(get_function_type('i', "p")));
gen_if_noreturn(builder->CreateICmpNE(builder->CreateCall(check_func, object), getInteger(32, 0)), [&](){
//static int counter = 0;
//builder->CreateCall3(get_global_function_vararg(printf, 'v', "ppi"),
// get_global((void*)"Illegal: %p %d\n", llvmType(getInt8Ty)), object, getInteger(32, counter++));
create_throw(E_IOBJECT);
}, "illegal_object", "legal_object");
}
}
//Same body as PopStaticExpression ...
void PopStaticVariableExpression::codegen(){
llvm::Value* value = val->codegen_get_value();
llvm::Value* var_addr = get_global((void*)addr, llvmType(getInt8Ty));
release_variable(type, var_addr);
variable_write(var_addr, value, type);
c_SP(-val->on_stack);
}
void PushUnknownExpression::codegen_on_stack(){
obj->codegen_on_stack();
store_pc(pc);
builder->CreateCall(get_global_function_jif(EXEC_push_unknown, 'v', "h"), getInteger(16, 0));
}
llvm::Value* PushUnknownExpression::codegen_get_value(){
codegen_on_stack();
return ret_top_stack(T_VARIANT, true);
}
llvm::Value* PushPureObjectVariableExpression::codegen_get_value(){
if (isa<PushSuperExpression>(obj)){
int offset = desc()->variable.offset;
llvm::Value* ret = read_variable_offset(type, current_op, getInteger(TARGET_BITS, offset));
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* object = obj->codegen_get_value();
make_double_nullcheck(object);
llvm::Value* object_ptr = extract_value(object, 1);
//We have a non-native object, so dispatch is not needed for effective class for checking
create_check(klass(), extract_value(object, 0), object_ptr);
llvm::Value* desc_variable = get_class_desc_entry(object_ptr, index);
llvm::Value* offset = to_target_int(builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_variable, getInteger(TARGET_BITS, offsetof(CLASS_DESC_VARIABLE, offset))), llvmType(getInt32PtrTy))));
llvm::Value* ret = read_variable_offset(type, object_ptr, offset);
unref_object_no_nullcheck(object_ptr);
c_SP(-obj->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type);
return ret;
}
void PopPureObjectVariableExpression::codegen(){
llvm::Value* value = val->codegen_get_value();
llvm::Value* object = obj->codegen_get_value();
make_double_nullcheck(object, [&](){
release(value, val->type);
});
llvm::Value* object_ptr = extract_value(object, 1);
//We have a non-native object, so dispatch is not needed for effective class for checking
create_check((CLASS*)(void*)obj->type, extract_value(object, 0), object_ptr);
llvm::Value* desc_variable = get_class_desc_entry(object_ptr, index);
llvm::Value* offset = to_target_int(builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_variable, getInteger(TARGET_BITS, offsetof(CLASS_DESC_VARIABLE, offset))), llvmType(getInt32PtrTy))));
llvm::Value* addr = builder->CreateGEP(object_ptr, offset);
release_variable(type, addr);
variable_write(addr, value, type);
unref_object_no_nullcheck(object_ptr);
c_SP(-val->on_stack-obj->on_stack);
}
llvm::Value* PushPureObjectConstantExpression::codegen_get_value(){
llvm::Value* object = obj->codegen_get_value();
if (obj->on_stack)
c_SP(-1);
llvm::Value* object_ptr = extract_value(object, 1);
make_nullcheck(object_ptr);
llvm::Value* desc_const = get_class_desc_entry(object_ptr, index);
llvm::Value* ret;
if (TYPE_is_string(type)){
llvm::Value* translated = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(desc_const, getInteger(TARGET_BITS, 2*sizeof(void*)+8))), llvmType(getInt1Ty));
llvm::Value* straddr = builder->CreateGEP(desc_const, getInteger(TARGET_BITS, offsetof(CLASS_DESC_CONSTANT, value)));
straddr = builder->CreateLoad(builder->CreateBitCast(straddr, llvmType(getInt8PtrTy)));
straddr = gen_if_phi(straddr, translated, [&](){
return builder->CreateCall(get_global_function(GB.Translate, 'p', "p"), straddr);
});
llvm::Value* len = builder->CreateCall(get_global_function(strlen, 'j', "p"), straddr);
if (TARGET_BITS == 64)
len = builder->CreateTrunc(len, llvmType(getInt32Ty));
ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), straddr, getInteger(32, 0), len);
} else {
llvm::Type* t = TYPE_llvm(type);
llvm::Value* addr = builder->CreateGEP(desc_const, getInteger(TARGET_BITS, offsetof(CLASS_DESC_CONSTANT, value)));
ret = builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(t)));
}
unref_object_no_nullcheck(object_ptr);
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* PushPureObjectPropertyExpression::codegen_private(bool get_value){
obj->codegen_on_stack();
llvm::Value* object = ret_top_stack(obj->type, true);
bool super = isa<PushSuperExpression>(obj);
if (!super)
make_double_nullcheck(object);
llvm::Value* object_ptr = extract_value(object, 1);
llvm::Value* class_desc_property;
llvm::Value* is_native;
if (super){
is_native = getInteger(1, klass()->is_native);
} else {
class_desc_property = get_class_desc_entry(object_ptr, index);
if (!klass()->table[index].desc->property.native)
is_native = getInteger(1, false); //Assume no children can be native either
else
is_native = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, 4*TARGET_BITS/8))), llvmType(getInt1Ty));
}
//FIXME dispatch:
create_check(klass(), extract_value(object, 0), object_ptr);
llvm::Value* ret = gen_if_else_phi(is_native, [&](){
llvm::Value* read = super ? get_global((void*)this->desc()->property.read, llvmType(getInt8Ty)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, read))), charPP));
llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"),
read,
object_ptr,
getInteger(TARGET_BITS, type),
get_nullptr());
gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
llvm::Value* ret = read_value(get_global(&TEMP, value_type), type);
borrow(ret, type);
return ret;
}, [&](){
llvm::Value* read = super ? (llvm::Value*)getInteger(32, (int)(intptr_t)this->desc()->property.read) : (llvm::Value*)builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, read))), llvmType(getInt32PtrTy)));
llvm::Value* klass = super ? builder->CreateIntToPtr(getInteger(TARGET_BITS, obj->type), llvmType(getInt8PtrTy)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, klass))), charPP));
builder->CreateStore(klass, get_global((void*)&EXEC.klass));
builder->CreateStore(object_ptr, get_global((void*)&EXEC.object));
builder->CreateStore(getInteger(32, 0), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty)));
builder->CreateStore(read, get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
/*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"),
get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 1));*/
builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', ""));
llvm::Value* ret = read_value(get_global(RP, value_type), type);
builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(RP, LONG_TYPE));
return ret;
}, "property_native", "property_non_native", "property_read_done");
unref_object_no_nullcheck(object_ptr);
if (get_value && !on_stack){
c_SP(-1);
return ret;
} else {
store_value(get_value_on_top_addr(), ret, type);
return ret;
}
}
void PopPureObjectPropertyExpression::codegen(){
llvm::Value* v = val->codegen_get_value();
obj->codegen_on_stack();
llvm::Value* object = ret_top_stack(obj->type, true);
bool super = isa<PushSuperExpression>(obj);
if (!super)
make_double_nullcheck(object);
llvm::Value* object_ptr = extract_value(object, 1);
llvm::Value* class_desc_property;
llvm::Value* is_native;
if (super){
is_native = getInteger(1, ((CLASS*)(void*)obj->type)->is_native);
} else {
CLASS* klass = (CLASS*)(void*)obj->type;
class_desc_property = get_class_desc_entry(object_ptr, index);
if (!klass->table[index].desc->property.native)
is_native = getInteger(1, false); //Assume no children can be native either
else
is_native = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, 4*TARGET_BITS/8))), llvmType(getInt1Ty));
}
//FIXME dispatch
create_check((CLASS*)(void*)obj->type, extract_value(object, 0), object_ptr);
gen_if_else(is_native, [&](){
llvm::Value* write = super ? get_global((void*)this->desc()->property.write, llvmType(getInt8Ty)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, write))), charPP));
llvm::Value* val_ptr = builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -2)), llvmType(getInt8PtrTy));
llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"),
write,
object_ptr,
getInteger(TARGET_BITS, 0),
val_ptr);
gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
release(v, val->type);
}, [&](){
push_value(v, val->type);
llvm::Value* val_ptr = builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -3)), pointer_t(LONG_TYPE));
builder->CreateStore(getInteger(TARGET_BITS, T_VOID), val_ptr);
llvm::Value* write = super ? (llvm::Value*)getInteger(32, (int)(intptr_t)this->desc()->property.write) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, write))), llvmType(getInt32PtrTy)));
llvm::Value* klass = super ? builder->CreateIntToPtr(getInteger(TARGET_BITS, obj->type), llvmType(getInt8PtrTy)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, klass))), charPP));
builder->CreateStore(klass, get_global((void*)&EXEC.klass));
builder->CreateStore(object_ptr, get_global((void*)&EXEC.object));
builder->CreateStore(getInteger(32, 1), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty)));
builder->CreateStore(write, get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
/*builder->CreateCall5(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)"%d %p %p %d\n", llvmType(getInt8Ty)), getInteger(32, 2), klass, class_desc_property, getInteger(32, index));*/
builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', ""));
//I think the release of RP is not needed, it should already be T_VOID...
}, "property_native", "property_non_native", "property_write_done");
unref_object_no_nullcheck(object_ptr);
c_SP(-2);
}
void PopVirtualPropertyExpression::codegen(){
llvm::Value* v = val->codegen_get_value();
llvm::Value* object = obj->codegen_get_value();
llvm::Value* object_ptr = extract_value(object, 1);
if (is_static){
//Only allowed on virtual classes
//Assume it returned a T_CLASS, or threw an error...
gen_if_noreturn(builder->CreateICmpNE(load_element(get_value_on_top_addr(), 0), getInteger(TARGET_BITS, T_CLASS)), [&](){
create_throw(E_STATIC, ((CLASS*)(void*)obj->type)->name, this->name);
});
object_ptr = get_nullptr();
} else
create_check((CLASS*)(void*)obj->type, get_global((void*)obj->type, llvmType(getInt8Ty)), object_ptr);
llvm::Value* write = get_global((void*)desc()->property.write, llvmType(getInt8Ty));
llvm::Value* val_ptr = builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -2)), llvmType(getInt8PtrTy));
llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"),
write,
object_ptr,
getInteger(TARGET_BITS, 0),
val_ptr);
gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
release(v, val->type);
if (!is_static)
unref_object_no_nullcheck(object_ptr);
c_SP(-2);
}
llvm::Value* PushVirtualPropertyExpression::codegen_private(bool get_value){
obj->codegen_on_stack();
llvm::Value* object = ret_top_stack(obj->type, true);
//Might be T_NULL or T_CLASS or the virtual class:
llvm::Value* valtype = builder->CreatePtrToInt(extract_value(object, 0), LONG_TYPE);
gen_if_noreturn(builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_NULL)), [&](){
create_throw(E_NULL);
});
//T_CLASS could be resolved at compile time, since only gb.db returns a (virtual) object
//when invoking a static read property/static method...
llvm::Value* object_ptr = builder->CreateSelect(
builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_CLASS)),
get_nullptr(),
extract_value(object, 1));
llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"),
get_global((void*)desc()->property.read, llvmType(getInt8Ty)), //Assume no override by inheritance
object_ptr,
getInteger(TARGET_BITS, type),
get_nullptr());
gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
llvm::Value* ret = read_value(get_global(&TEMP, value_type), type);
borrow(ret, type);
unref_object(object_ptr);
if (get_value && !on_stack){
c_SP(-1);
return ret;
} else {
store_value(get_value_on_top_addr(), ret, type);
return ret;
}
}
llvm::Value* PushPureObjectStaticPropertyExpression::codegen_private(bool get_value){
obj->codegen_on_stack();
//Assume it returned a T_CLASS, or threw an error...
gen_if_noreturn(builder->CreateICmpNE(load_element(get_value_on_top_addr(), 0), getInteger(TARGET_BITS, T_CLASS)), [&](){
create_throw(E_STATIC, this->klass()->name, this->name);
});
llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"),
get_global((void*)desc()->property.read, llvmType(getInt8Ty)), //Assume no override by inheritance
get_nullptr(),
getInteger(TARGET_BITS, type),
get_nullptr());
gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
llvm::Value* ret = read_value(get_global(&TEMP, value_type), type);
borrow(ret, type);
if (get_value && !on_stack){
c_SP(-1);
return ret;
} else {
store_value(get_value_on_top_addr(), ret, type);
return ret;
}
}
llvm::Value* PushStaticPropertyExpression::codegen_get_value(){
CLASS* c = ((PushClassExpression*)obj)->klass;
CLASS_DESC* desc = c->table[index].desc;
llvm::Value* ret;
if (desc->property.native){
llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"),
get_global((void*)desc->property.read, llvmType(getInt8Ty)),
get_nullptr(),
getInteger(TARGET_BITS, type),
get_nullptr());
gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
ret = read_value(get_global(&TEMP, value_type), type);
borrow(ret, type);
} else {
builder->CreateStore(get_global((void*)desc->property.klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass));
builder->CreateStore(get_nullptr(), get_global((void*)&EXEC.object));
builder->CreateStore(getInteger(32, 0), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty)));
builder->CreateStore(getInteger(32, (int)(intptr_t)desc->property.read), get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
/*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"),
get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 3));*/
builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', ""));
ret = read_value(get_global(RP, value_type), type);
builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(RP, LONG_TYPE));
}
if (on_stack)
push_value(ret, type);
return ret;
}
void PopStaticPropertyExpression::codegen(){
llvm::Value* value = val->codegen_get_value();
CLASS_DESC* desc = klass->table[index].desc;
if (desc->property.native){
llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"),
get_global((void*)desc->property.write, llvmType(getInt8Ty)),
get_nullptr(),
getInteger(TARGET_BITS, type),
builder->CreateBitCast(get_value_on_top_addr(), llvmType(getInt8PtrTy)));
gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
release(value, type);
c_SP(-val->on_stack);
} else {
builder->CreateStore(get_global((void*)desc->property.klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass));
builder->CreateStore(get_nullptr(), get_global((void*)&EXEC.object));
builder->CreateStore(getInteger(32, 1), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty)));
builder->CreateStore(getInteger(32, (int)(intptr_t)desc->property.write), get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
/*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"),
get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 4));*/
builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', ""));
}
}
void PopUnknownPropertyUnknownExpression::codegen(){
val->codegen_on_stack();
llvm::Value* effective_class;
llvm::Value* object;
if (PushClassExpression* pce = dyn_cast<PushClassExpression>(obj)){
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)pce->klass), llvmType(getInt8PtrTy));
object = get_nullptr();
push_value(NULL, T_VOID);
} else {
llvm::Value* ob = obj->codegen_get_value();
object = extract_value(ob, 1);
CLASS* k = (CLASS*)(void*)obj->type;
if (isa<PushSuperExpression>(obj)){
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy));
} else if (!k->is_virtual){
make_nullcheck(object);
effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0);
} else {
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy));
}
create_check(k, effective_class, object);
}
builder->CreateCall3(get_global_function(JR_pop_unknown_property_unknown, 'v', "ppj"),
effective_class, object, getInteger(TARGET_BITS, (uint64_t)(void*)name));
}
void PopUnknownExpression::codegen(){
val->codegen_on_stack();
obj->codegen_on_stack();
store_pc(pc);
builder->CreateCall(get_global_function_jif(EXEC_pop_unknown, 'v', ""));
}
llvm::Value* PushDynamicExpression::codegen_get_value(){
llvm::Value* ret;
if (ctype->id == TC_ARRAY){
/*llvm::Value* obj = builder->CreateCall4(get_global_function_jif(CARRAY_create_static, 'p', "pppp"),
get_global((void*)CP, llvmType(getInt8Ty)),
current_op,
get_global(CP->load->array[ctype->value], llvmType(getInt8Ty)),
builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset)));
borrow_object_no_nullcheck(obj);
ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), obj);*/
ret = codegen_tc_array(CP, current_op, ctype->value, builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset)), type);
} else if (ctype->id == TC_STRUCT){
ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"),
get_global((void*)CP, llvmType(getInt8Ty)),
builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)),
builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset)));
borrow_object_no_nullcheck(ret);
return get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret);
} else {
ret = read_variable_offset(type, current_op, getInteger(TARGET_BITS, offset));
}
if (on_stack)
push_value(ret, type);
return ret;
}
void PopDynamicExpression::codegen(){
llvm::Value* new_val = val->codegen_get_value();
c_SP(-val->on_stack);
release_variable(type, builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset)));
write_variable_offset(type, current_op, offset, new_val);
}
//Same body as PopStaticVariableExpression ...
void PopStaticExpression::codegen(){
llvm::Value* new_val = val->codegen_get_value();
llvm::Value* ad = get_global((void*)addr, llvmType(getInt8Ty));
release_variable(type, ad);
variable_write(ad, new_val, type);
c_SP(-val->on_stack);
}
llvm::Value* PushFunctionExpression::codegen_get_value(){
llvm::Value* func = insert_value(get_new_struct(function_type), current_op, 1);
borrow_object(current_op);
push_value(func, T_FUNCTION);
return func;
}
void PushFunctionExpression::codegen_on_stack(){
//llvm::Value* func = get_new_struct(function_type, read_global((void*)&CP), current_op, getInteger(8, FUNCTION_PRIVATE), getInteger(8, true), getInteger(16, index));
llvm::Value* func = insert_value(get_new_struct(function_type), current_op, 1);
borrow_object(current_op);
push_value(func, T_FUNCTION);
}
void PushClassExpression::codegen_on_stack(){
llvm::Value* sp = read_global((void*)&SP, pointer_t(value_types[T_CLASS]));
store_element(sp, 0, getInteger(TARGET_BITS, T_CLASS));
store_element(sp, 1, get_global((void*)klass, llvmType(getInt8Ty)));
c_SP(1);
}
llvm::Value* AddQuickExpression::codegen_get_value(){
if (type == T_VARIANT){
expr->codegen_on_stack();
builder->CreateCall(get_global_function(JR_aq_variant, 'v', "i"), getInteger(32, add));
return ret_top_stack(T_VARIANT, on_stack);
}
llvm::Value* orig = expr->codegen_get_value();
if (expr->on_stack) c_SP(-1);
llvm::Value* new_val;
switch(type){
case T_BYTE: new_val = builder->CreateAdd(orig, getInteger(8, add)); break;
case T_SHORT: new_val = builder->CreateAdd(orig, getInteger(16, add)); break;
case T_INTEGER: new_val = builder->CreateAdd(orig, getInteger(32, add)); break;
case T_LONG: new_val = builder->CreateAdd(orig, getInteger(64, add)); break;
case T_SINGLE: new_val = builder->CreateFAdd(orig, getFloat((float)add)); break;
case T_FLOAT: new_val = builder->CreateFAdd(orig, getFloat((double)add)); break;
case T_POINTER: new_val = builder->CreateGEP(orig, getInteger(TARGET_BITS, add)); break;
default: __builtin_unreachable();
}
if (on_stack)
push_value(new_val, type);
return new_val;
}
llvm::Value* PushMeExpression::codegen_get_value(){
//llvm::Value* ret = get_new_struct(object_type, read_global((void*)CP), current_op);
llvm::Value* ret = get_new_struct(object_type, get_global((void*)CP, llvmType(getInt8Ty)), current_op);
borrow_object_no_nullcheck(current_op);
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* PushSuperExpression::codegen_get_value(){
llvm::Value* ret = get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), current_op);
borrow_object_no_nullcheck(current_op);
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* PushLocalExpression::codegen_get_value(){
llvm::Value* ret = builder->CreateLoad(locals[index]);
if (on_stack)
push_value(ret, type);
if (no_ref_variant == false)
borrow(ret, type);
return ret;
}
void PushLocalExpression::codegen_on_stack(){
llvm::Value* ret = builder->CreateLoad(locals[index]);
push_value(ret, type);
borrow(ret, type); //Is this better optimized?
//borrow_top(type);
}
llvm::Value* PushParamExpression::codegen_get_value(){
llvm::Value* ret = builder->CreateLoad(params[index + FP->n_param]);
if (on_stack)
push_value(ret, type);
borrow(ret, type);
return ret;
}
void PushParamExpression::codegen_on_stack(){
llvm::Value* ret = builder->CreateLoad(params[index + FP->n_param]);
push_value(ret, type);
borrow(ret, type);
//borrow_top(type);
}
void PopLocalExpression::codegen(){
llvm::Value* v = val->codegen_get_value();
if (val->on_stack) c_SP(-1);
llvm::Value* old_val = builder->CreateLoad(locals[index]);
release(old_val, type);
builder->CreateStore(v, locals[index]);
if (type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){
if (TYPE_is_object(type)){
//codegen_printf("%p\n", extract_value(v, 0));
}
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type, false);
} else if (type == T_VARIANT && val->no_ref_variant){
llvm::Value* vtype = extract_value(old_val, 0);
llvm::Value* is_str = builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_STRING));
llvm::Value* is_obj = builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT));
gen_if(builder->CreateOr(is_str, is_obj), [&](){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, get_default(T_VARIANT), T_VARIANT, false);
});
}
}
void PopParamExpression::codegen(){
llvm::Value* v = val->codegen_get_value();
if (val->on_stack) c_SP(-1);
llvm::Value* old_val = builder->CreateLoad(params[index + FP->n_param]);
release(old_val, type);
builder->CreateStore(v, params[index + FP->n_param]);
if (type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type, false);
} else if (type == T_VARIANT && val->no_ref_variant){
llvm::Value* vtype = extract_value(old_val, 0);
llvm::Value* is_str = builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_STRING));
llvm::Value* is_obj = builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT));
gen_if(builder->CreateOr(is_str, is_obj), [&](){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, get_default(T_VARIANT), T_VARIANT, false);
});
}
}
static llvm::AllocaInst* create_alloca_in_entry(llvm::Type* t){
llvm::BasicBlock* bb = builder->GetInsertBlock();
builder->SetInsertPoint(entry_block, entry_block->begin());
llvm::AllocaInst* ret = builder->CreateAlloca(t);
builder->SetInsertPoint(bb);
return ret;
}
static llvm::AllocaInst* create_alloca_in_entry_init_default(llvm::Type* t, TYPE type){
llvm::BasicBlock* bb = builder->GetInsertBlock();
builder->SetInsertPoint(entry_block, entry_block->begin());
llvm::AllocaInst* ret = builder->CreateAlloca(t);
store_default(ret, type);
builder->SetInsertPoint(bb);
return ret;
}
static llvm::Value* release_ctrl(int index){
llvm::Value* old_type = builder->CreateLoad(current_ctrl_types[index - FP->n_local]);
if (is_ctrl_type_used(1, index)){
gen_if(builder->CreateICmpEQ(old_type, getInteger(32, 1)), [&](){
release(builder->CreateLoad(ctrl_values[index - FP->n_local][1]), T_STRING);
}, "was_string_ctrl_before");
}
if (is_ctrl_type_used(2, index)){
gen_if(builder->CreateICmpEQ(old_type, getInteger(32, 2)), [&](){
release(builder->CreateLoad(ctrl_values[index - FP->n_local][2]), T_OBJECT);
}, "was_object_ctrl_before");
}
if (is_ctrl_type_used(3, index)){
gen_if(builder->CreateICmpEQ(old_type, getInteger(32, 3)), [&](){
release(builder->CreateLoad(ctrl_values[index - FP->n_local][3]), T_VARIANT);
}, "was_variant_ctrl_before");
}
return old_type;
}
static void set_ctrl(llvm::Value* v, TYPE type, int index){
llvm::Value* old_type = release_ctrl(index);
int type_next = special_ctrl_type(type);
builder->CreateStore(getInteger(32, type_next), current_ctrl_types[index - FP->n_local]);
set_ctrl_type(type, index);
if (type_next == 0){
if (locals[index] == NULL || locals[index]->getType() != TYPE_llvm(type))
locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type);
} else {
locals[index] = ctrl_values[index - FP->n_local][type_next];
}
if (type == T_STRING || type == T_VARIANT || TYPE_is_object(type)){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type);
} else {
gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type);
}, "old_ctrl_needs_to_be_cleaned2");
}
if (type != T_NULL)
builder->CreateStore(v, locals[index]);
#if 0
TYPE old_ctrl_type = get_ctrl_type(index);
set_ctrl_type(type, index);
if (old_ctrl_type != T_VOID && old_ctrl_type != T_NULL)
release(builder->CreateLoad(locals[index]), old_ctrl_type);
if (type == T_NULL) return;
if (old_ctrl_type != type){
locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type);
}
builder->CreateStore(v, locals[index]);
if (old_ctrl_type == T_STRING || old_ctrl_type == T_VARIANT || TYPE_is_object(old_ctrl_type) ||
type == T_STRING || type == T_VARIANT || TYPE_is_object(type)){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type);
}
#endif
/*builder->CreateStore(v, locals[index]);
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type);*/
}
static void codegen_pop_ctrl(llvm::Value* v, Expression* val, int index){
TYPE type = val->type;
llvm::Value* old_type = release_ctrl(index);
int type_next = special_ctrl_type(type);
builder->CreateStore(getInteger(32, type_next), current_ctrl_types[index - FP->n_local]);
set_ctrl_type(type, index);
if (type_next == 0){
if (locals[index] == NULL || locals[index]->getType() != TYPE_llvm(type))
locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type);
} else {
locals[index] = ctrl_values[index - FP->n_local][type_next];
}
if (type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type);
} else {
gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type);
}, "old_ctrl_needs_to_be_cleaned3");
}
if (val->on_stack) c_SP(-1);
if (type != T_NULL)
builder->CreateStore(v, locals[index]);
/*
//
TYPE type = val->type;
TYPE old_ctrl_type = get_ctrl_type(index);
set_ctrl_type(type, index);
if (val->on_stack) c_SP(-1);
if (old_ctrl_type != T_VOID && old_ctrl_type != T_NULL)
release(builder->CreateLoad(locals[index]), old_ctrl_type);
if (type == T_NULL) return;
if (old_ctrl_type != type){
locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type);
}
builder->CreateStore(v, locals[index]);
if (old_ctrl_type == T_STRING || old_ctrl_type == T_VARIANT || TYPE_is_object(old_ctrl_type) ||
type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type);
}
*/
}
static void codegen_pop_ctrl(Expression* val, int index){
codegen_pop_ctrl(val->codegen_get_value(), val, index);
return;
/*TYPE type = val->type;
TYPE old_ctrl_type = get_ctrl_type(index);
set_ctrl_type(type, index);
llvm::Value* v = val->codegen_get_value();
if (val->on_stack) c_SP(-1);
if (old_ctrl_type != T_VOID && old_ctrl_type != T_NULL)
release(builder->CreateLoad(locals[index]), old_ctrl_type);
if (type == T_NULL) return;
if (old_ctrl_type != type){
locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type);
}
builder->CreateStore(v, locals[index]);
if (old_ctrl_type == T_STRING || old_ctrl_type == T_VARIANT || TYPE_is_object(old_ctrl_type) ||
type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE)));
store_value(stack_addr, v, type);
}*/
}
//Same code as PopLocalExpression::codegen
void PopCtrlExpression::codegen(){
if (!isa<PushClassExpression>(val))
codegen_pop_ctrl(val, index);
}
void PopOptionalExpression::codegen(){
llvm::Value* addr = builder->CreateBitCast(builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, index*sizeof(VALUE))), pointer_t(LONG_TYPE));
llvm::Value* t = builder->CreateLoad(addr);
gen_if(builder->CreateICmpEQ(t, getInteger(TARGET_BITS, T_VOID)), [&](){
if (is_default){
store_default(params[FP->n_param + index], type);
} else {
llvm::Value* value = val->codegen_get_value();
store_value(addr, value, type);
builder->CreateStore(value, params[FP->n_param + index]);
if (val->on_stack) c_SP(-1);
}
}, "not_passed", "passed_or_done");
}
llvm::Value* PushAutoCreateExpression::codegen_get_value(){
llvm::Value* ret = builder->CreateCall2(get_global_function(GB.AutoCreate, 'p', "pi"),
get_global((void*)klass, llvmType(getInt8Ty)), getInteger(32, 0));
borrow_object_no_nullcheck(ret);
ret = get_new_struct(object_type,
get_global((void*)klass, llvmType(getInt8Ty)), ret);
if (on_stack)
push_value(ret, type);
return ret;
}
void PushPureObjectUnknownExpression::codegen_on_stack(){
llvm::Value* ob = obj->codegen_get_value();
llvm::Value* object = extract_value(ob, 1);
CLASS* k = (CLASS*)(void*)obj->type;
llvm::Value* effective_class;
if (isa<PushSuperExpression>(obj)){
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy));
} else if (!k->is_virtual){
make_nullcheck(object);
effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0);
} else {
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy));
}
create_check(k, effective_class, object);
/*llvm::Value* func = insert_value(get_new_struct(function_type), object, 1);*/
builder->CreateCall4(get_global_function(JR_push_unknown_property_unknown, 'v', "pipp"),
builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)name), llvmType(getInt8PtrTy)),
getInteger(32, name_id),
effective_class,
object);
}
llvm::Value* PushPureObjectUnknownExpression::codegen_get_value(){
codegen_on_stack();
return ret_top_stack(T_VARIANT, true);
}
void PushStaticUnknownExpression::codegen_on_stack(){
builder->CreateCall4(get_global_function(JR_push_unknown_property_unknown, 'v', "pipp"),
builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)name), llvmType(getInt8PtrTy)),
getInteger(32, name_id),
builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)klass), llvmType(getInt8PtrTy)),
get_nullptr());
}
llvm::Value* PushStaticUnknownExpression::codegen_get_value(){
codegen_on_stack();
return ret_top_stack(T_VARIANT, true);
}
llvm::Value* PushPureObjectFunctionExpression::codegen_get_value(){
llvm::Value* ob = obj->codegen_get_value();
llvm::Value* object = extract_value(ob, 1);
CLASS* k = klass();
CLASS_DESC* d = desc();
if (isa<PushSuperExpression>(obj)){
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy));
} else if (!k->is_virtual){
make_nullcheck(object);
effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0);
} else {
//FIXME Doesn't PushVirtualFunctionExpression take care of this?
//CLASS_load(k);
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy));
}
create_check(k, effective_class, object);
llvm::Value* func = insert_value(get_new_struct(function_type), object, 1);
if (!d->method.native){
//Assume native classes don't inherit non-native ones :)
} else {
int offset_table = TARGET_BITS == 64 ? 40/8 : 28/4;
int offset_desc_in_desc_symbol = TARGET_BITS == 64 ? 12 : 8;
int offset_native_flag = TARGET_BITS == 64 ? 35 : 19;
//table_addr = (char*)effective_class->table
llvm::Value* table_addr = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, pointer_t(llvmType(getInt8PtrTy))), getInteger(TARGET_BITS, offset_table)));
//desc_addr_addr = (char*)(table_addr + sizeof(CLASS_DESC_SYMBOL) * index + desc_offset)
llvm::Value* desc_addr_addr = builder->CreateGEP(table_addr, getInteger(TARGET_BITS, sizeof(CLASS_DESC_SYMBOL) * index + offset_desc_in_desc_symbol));
//desc_addr = *(char**)desc_addr_addr
llvm::Value* desc_addr = builder->CreateLoad(builder->CreateBitCast(desc_addr_addr, pointer_t(llvmType(getInt8PtrTy))));
//native_flag_addr = (char*)(desc_addr + offset_native_flag)
llvm::Value* native_flag_addr = builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offset_native_flag));
//native_subr_flag = *native_flag_addr
llvm::Value* native_subr_flag = builder->CreateLoad(native_flag_addr);
//native_flag = (bool)(native_subr_flag & 1)
llvm::Value* native_flag = builder->CreateTrunc(native_subr_flag, llvmType(getInt1Ty));
//subr_flag = (bool)(native_subr_flag & 2)
//llvm::Value* subr_flag = builder->CreateICmpNE(builder->CreateAnd(native_subr_flag, getInteger(8, 2)), getInteger(8, 0));
//llvm::Value* function_kind = builder->CreateSelect(native_flag, builder->CreateSelect(subr_flag, getInteger(8, FUNCTION_SUBR), getInteger(8, FUNCTION_NATIVE)), getInteger(8, FUNCTION_PUBLIC));
llvm::Value* function_kind = builder->CreateSelect(native_flag, getInteger(8, FUNCTION_NATIVE), getInteger(8, FUNCTION_PUBLIC));
///Disable subr because it is only meant to be used static
func = insert_value(func, function_kind, 2);
}
if (obj->on_stack)
c_SP(-1);
if (on_stack)
push_value(func, T_FUNCTION);
return func;
}
llvm::Value* PushVirtualFunctionExpression::codegen_get_value(){
llvm::Value* ob = obj->codegen_get_value();
llvm::Value* object = extract_value(ob, 1);
CLASS* k = klass();
effective_class = get_global((void*)k, llvmType(getInt8Ty));
create_check(k, effective_class, object);
llvm::Value* func = insert_value(insert_value(get_new_struct(function_type), object, 1), getInteger(8, FUNCTION_NATIVE), 2);
if (obj->on_stack)
c_SP(-1);
if (on_stack)
push_value(func, T_FUNCTION);
return func;
}
llvm::Value* PushVirtualStaticFunctionExpression::codegen_get_value(){
llvm::Value* ob = obj->codegen_get_value();
llvm::Value* object = extract_value(ob, 1);
CLASS* k = klass();
effective_class = get_global((void*)k, llvmType(getInt8Ty));
llvm::Value* func = insert_value(insert_value(get_new_struct(function_type), object, 1), getInteger(8, FUNCTION_NATIVE), 2);
if (obj->on_stack)
c_SP(-1);
if (on_stack)
push_value(func, T_FUNCTION);
return func;
}
//Almost the same code as above, but setting object to null ...
llvm::Value* PushPureObjectStaticFunctionExpression::codegen_get_value(){
llvm::Value* ob = obj->codegen_get_value();
llvm::Value* object = extract_value(ob, 1);
CLASS* k = klass();
CLASS_DESC* d = desc();
if (isa<PushSuperExpression>(obj)){
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy));
} else if (!k->is_virtual){
make_nullcheck(object);
effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0);
} else {
//CLASS_load(k);
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy));
}
create_check(k, effective_class, object);
unref_object(object);
object = get_nullptr();
llvm::Value* func = insert_value(get_new_struct(function_type), object, 1);
if (!d->method.native){
//Assume native classes don't inherit non-native ones :)
} else {
int offset_table = TARGET_BITS == 64 ? 40/8 : 28/4;
int offset_desc_in_desc_symbol = TARGET_BITS == 64 ? 12 : 8;
int offset_native_flag = TARGET_BITS == 64 ? 35 : 19;
//table_addr = (char*)effective_class->table
llvm::Value* table_addr = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, pointer_t(llvmType(getInt8PtrTy))), getInteger(TARGET_BITS, offset_table)));
//desc_addr_addr = (char*)(table_addr + sizeof(CLASS_DESC_SYMBOL) * index + desc_offset)
llvm::Value* desc_addr_addr = builder->CreateGEP(table_addr, getInteger(TARGET_BITS, sizeof(CLASS_DESC_SYMBOL) * index + offset_desc_in_desc_symbol));
//desc_addr = *(char**)desc_addr_addr
llvm::Value* desc_addr = builder->CreateLoad(builder->CreateBitCast(desc_addr_addr, pointer_t(llvmType(getInt8PtrTy))));
//native_flag_addr = (char*)(desc_addr + offset_native_flag)
llvm::Value* native_flag_addr = builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offset_native_flag));
//native_subr_flag = *native_flag_addr
llvm::Value* native_subr_flag = builder->CreateLoad(native_flag_addr);
//native_flag = (bool)(native_subr_flag & 1)
llvm::Value* native_flag = builder->CreateTrunc(native_subr_flag, llvmType(getInt1Ty));
//subr_flag = (bool)(native_subr_flag & 2)
//llvm::Value* subr_flag = builder->CreateICmpNE(builder->CreateAnd(native_subr_flag, getInteger(8, 2)), getInteger(8, 0));
//llvm::Value* function_kind = builder->CreateSelect(native_flag, builder->CreateSelect(subr_flag, getInteger(8, FUNCTION_SUBR), getInteger(8, FUNCTION_NATIVE)), getInteger(8, FUNCTION_PUBLIC));
llvm::Value* function_kind = builder->CreateSelect(native_flag, getInteger(8, FUNCTION_NATIVE), getInteger(8, FUNCTION_PUBLIC));
///Disable subr, because I think it will never be used this way anyway, else it is a (big) performance hit.
func = insert_value(func, function_kind, 2);
}
if (obj->on_stack)
c_SP(-1);
if (on_stack)
push_value(func, T_FUNCTION);
return func;
}
llvm::Value* PushStaticFunctionExpression::codegen_get_value(){
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)klass()), llvmType(getInt8PtrTy));
llvm::Value* ret = get_new_struct(function_type, effective_class, get_nullptr());
if (on_stack)
push_value(ret, T_FUNCTION);
return ret;
}
llvm::Value* PushPureObjectStructFieldExpression::codegen_get_value(){
llvm::Value* struct_obj = extract_value(obj->codegen_get_value(), 1);
make_nullcheck(struct_obj);
llvm::Value* ref_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTRUCT, ref))), charPP);
llvm::Value* element_addr = gen_if_else_phi(builder->CreateICmpNE(builder->CreateLoad(ref_addr), get_nullptr()), [&](){
llvm::Value* addr_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTATICSTRUCT, addr))), charPP);
return builder->CreateGEP(builder->CreateLoad(addr_addr), getInteger(TARGET_BITS, this->desc()->variable.offset));
}, [&](){
return builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, sizeof(CSTRUCT) + this->desc()->variable.offset));
});
llvm::Value* ret;
int ctype_id = desc()->variable.ctype.id;
if (ctype_id == TC_ARRAY){
//Embedded array inside struct
ret = codegen_tc_array(desc()->variable.klass, struct_obj, desc()->variable.ctype.value, element_addr, type);
} else if (ctype_id == TC_STRUCT){
//Embedded struct inside struct
ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"),
get_global((void*)desc()->variable.klass, llvmType(getInt8Ty)),
builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)),
element_addr);
borrow_object_no_nullcheck(ret);
ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret);
} else {
ret = read_variable(type, element_addr);
}
unref_object_no_nullcheck(struct_obj);
c_SP(-obj->on_stack);
if (on_stack)
push_value(ret, type);
return ret;
}
void PopPureObjectStructFieldExpression::codegen(){
llvm::Value* value = val->codegen_get_value();
llvm::Value* struct_obj = extract_value(obj->codegen_get_value(), 1);
make_nullcheck(struct_obj);
llvm::Value* ref_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTRUCT, ref))), charPP);
llvm::Value* element_addr = gen_if_else_phi(builder->CreateICmpNE(builder->CreateLoad(ref_addr), get_nullptr()), [&](){
llvm::Value* addr_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTATICSTRUCT, addr))), charPP);
return builder->CreateGEP(builder->CreateLoad(addr_addr), getInteger(TARGET_BITS, this->desc()->variable.offset));
}, [&](){
return builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, sizeof(CSTRUCT) + this->desc()->variable.offset));
});
release_variable(type, element_addr);
variable_write(element_addr, value, type);
unref_object_no_nullcheck(struct_obj);
}
llvm::Value* SwapExpression::codegen_get_value(){
llvm::Value* ret = push_a_expr->codegen_get_value();
pop_a_expr->codegen();
return ret;
}
void SwapExpression::codegen_on_stack(){
push_a_expr->codegen_on_stack();
pop_a_expr->codegen();
}
static llvm::Type* const extern_types[] = {
llvmType(getVoidTy),
llvmType(getInt8Ty),
llvmType(getInt8Ty),
llvmType(getInt16Ty),
llvmType(getInt32Ty),
llvmType(getInt64Ty),
llvmType(getFloatTy),
llvmType(getDoubleTy),
NULL,
llvmType(getInt8PtrTy),
llvmType(getInt8PtrTy),
llvmType(getInt8PtrTy),
NULL,
NULL,
NULL,
llvmType(getInt8PtrTy),
llvmType(getInt8PtrTy)
};
llvm::Value* codegen_extern_manage_value(llvm::Value* val, TYPE type){
if (type == T_BOOLEAN)
val = builder->CreateZExt(val, llvmType(getInt8Ty));
else if (TYPE_is_string(type))
val = builder->CreateGEP(extract_value(val, 1), to_target_int(extract_value(val, 2)));
else if (TYPE_is_object(type)){
val = extract_value(val, 1);
val = gen_if_phi(get_nullptr(), builder->CreateICmpNE(val, get_nullptr()), [&](){
CLASS* object_class = (CLASS*)(void*)type;
llvm::Value* normal = builder->CreateGEP(val, getInteger(TARGET_BITS, sizeof(OBJECT)));
llvm::Value* OBJ = builder->CreateBitCast(val, pointer_t(OBJECT_type));
llvm::Value* klass = load_element(OBJ, 0);
auto get_bit_from_class = [](llvm::Value* obj, int offset_byte, int offset_bit){
return builder->CreateTrunc(builder->CreateLShr(builder->CreateLoad(builder->CreateGEP(obj, getInteger(TARGET_BITS, offset_byte))), getInteger(8, offset_bit)), llvmType(getInt1Ty));
};
auto handle_class_object = [normal, &get_bit_from_class](llvm::Value* obj){
const int offset_is_native = TARGET_BITS == 64 ? 34 : 22;
const int bit_index_is_native = 2;
llvm::Value* is_native = get_bit_from_class(obj, offset_is_native, bit_index_is_native);
return gen_if_phi(normal, builder->CreateXor(is_native, getInteger(1, true)), [obj](){
llvm::Value* stat = builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, offsetof(CLASS, stat))), charPP);
return builder->CreateLoad(stat);
}, "not_native");
};
auto handle_struct_object = [](llvm::Value* obj){
/*if (((CSTRUCT *)ob)->ref)
addr = (char *)((CSTATICSTRUCT *)ob)->addr;
else
addr = (char *)ob + sizeof(CSTRUCT);*/
llvm::Value* ref_addr = builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, offsetof(CSTRUCT, ref))), charPP);
llvm::Value* ref_not_null = builder->CreateICmpNE(builder->CreateLoad(ref_addr), get_nullptr());
return gen_if_phi(builder->CreateGEP(obj, getInteger(TARGET_BITS, sizeof(CSTRUCT))), ref_not_null, [&](){
llvm::Value* addr_addr = builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, offsetof(CSTATICSTRUCT, addr))), charPP);
return builder->CreateLoad(addr_addr);
});
};
if (TYPE_is_pure_object(type) && object_class == (CLASS*)(void*)GB.FindClass("Class")){
val = handle_class_object(val);
} else if (TYPE_is_pure_object(type) && CLASS_is_array(object_class)){
val = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(val, getInteger(TARGET_BITS, offsetof(CARRAY, data))), charPP));
} else if (TYPE_is_pure_object(type) && CLASS_is_struct(object_class)){
val = handle_struct_object(val);
} else if (TYPE_is_pure_object(type)){
val = normal;
} else {
val = gen_if_else_phi(builder->CreateICmpEQ(klass, builder->CreateIntToPtr(getInteger(TARGET_BITS, GB.FindClass("Class")), llvmType(getInt8PtrTy))), [&](){
return handle_class_object(val);
}, [&](){
const int offset_is_array = TARGET_BITS == 64 ? 34 : 22;
const int bit_index_is_array = 6;
return gen_if_else_phi(get_bit_from_class(klass, offset_is_array, bit_index_is_array), [&](){
return builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(val, getInteger(TARGET_BITS, offsetof(CARRAY, data))), charPP));
}, [&](){
const int offset_is_struct = TARGET_BITS == 64 ? 34 : 22;
const int bit_index_is_struct = 5;
return gen_if_phi(normal, get_bit_from_class(klass, offset_is_struct, bit_index_is_struct), [&](){
return handle_struct_object(val);
}, "extern_arg_is_struct");
}, "extern_arg_is_array");
}, "extern_arg_is_class");
}
return val;
}, "OBJ_not_null_for_extern");
}
return val;
}
static llvm::Value* codegen_extern_manage_return_value(llvm::Value* ret, TYPE type){
if (type == T_BOOLEAN)
ret = builder->CreateICmpNE(ret, getInteger(8, false));
else if (TYPE_is_string(type)){
ret = gen_if_phi(get_default(T_CSTRING), builder->CreateICmpNE(ret, get_nullptr()), [&](){
return get_cstring_from_addr(ret);
}, "extern_return_not_nullstring");
} else if (TYPE_is_object(type)){
if (TYPE_is_pure_object(type)){
CLASS* class_struct = (CLASS*)(void*)type;
if (CLASS_is_struct(class_struct)){
ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"),
get_global((void*)-1, llvmType(getInt8Ty)),
get_global((void*)class_struct, llvmType(getInt8Ty)),
ret);
}
}
borrow_object(ret);
ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret);
}
return ret;
}
static void func_extern_call_variant_vararg(void* return_value_addr, void* func_addr, int nargs, TYPE return_type);
static llvm::Value* codegen_raise_event(std::vector<Expression*>& args, int index, bool on_stack){
for(size_t i=0, e=args.size(); i!=e; i++){
args[i]->codegen_on_stack();
}
llvm::Value* event_stop = builder->CreateCall3(get_global_function_vararg(GB.Raise, 'c', "pii"),
current_op, getInteger(32, index), getInteger(32, -args.size()));
event_stop = builder->CreateTrunc(event_stop, llvmType(getInt1Ty));
if (on_stack)
push_value(event_stop, T_BOOLEAN);
return event_stop;
}
llvm::Value* CallExpression::codegen_get_value(){
if (func->type == T_CLASS && desc == NULL){
//Cast
llvm::Value* ret = args[0]->codegen_get_value();
if (!args[0]->on_stack && on_stack){
push_value(ret, type);
} else if (args[0]->on_stack && !on_stack){
c_SP(-1);
}
return ret;
}
if (FunctionExpression* fe = dynamic_cast<FunctionExpression*>(func)){
if (fe->function_expr_type == EventFn){
return codegen_raise_event(args, ((PushEventExpression*)func)->index, on_stack);
}
}
if (PushExternExpression* ee = dynamic_cast<PushExternExpression*>(func)){
llvm::Value* call_addr;
if (ee->object_to_release){
llvm::Value* obj = ee->object_to_release->codegen_get_value();
c_SP(-ee->object_to_release->on_stack);
call_addr = builder->CreateCall2(get_global_function(JR_extern_dispatch_object, 'p', "pi"), extract_value(obj, 1), getInteger(32, ee->index));
}
CLASS_EXTERN* ext;
if (ee->object_to_release)
ext = &ee->klass->load->ext[ee->klass->table[ee->index].desc->ext.exec]; //An extern method with the correct signature
else
ext = &ee->klass->load->ext[ee->index];
EXTERN_FUNC_INFO func = JIF.F_EXTERN_get_function_info(ext);
llvm::Value* ret = NULL;
if (!ee->must_variant_dispatch){
std::vector<llvm::Type*> ft;
std::vector<llvm::Value*> orig_args;
std::vector<llvm::Value*> func_args;
ft.resize(ext->n_param);
orig_args.resize(args.size());
func_args.resize(args.size());
for(size_t i=0; i<args.size(); i++){
if (i < (size_t)ext->n_param)
ft[i] = extern_types[ext->param[i].type];
llvm::Value* val = args[i]->codegen_get_value();
orig_args[i] = val;
val = codegen_extern_manage_value(val, args[i]->type);
func_args[i] = val;
}
llvm::FunctionType* function_type = llvm::FunctionType::get(extern_types[type > T_OBJECT ? T_OBJECT : type], ft, true);
std::string function_name = ext->library;
function_name += '.';
function_name += func.alias;
llvm::Value* call_function;
if (!ee->object_to_release){
llvm::GlobalValue* glob_val = (llvm::GlobalValue*)M->getOrInsertFunction(function_name, function_type);
register_global_symbol(function_name, glob_val, func.call);
call_function = glob_val;
} else {
call_function = builder->CreateBitCast(call_addr, pointer_t(function_type));
}
ret = builder->CreateCall(call_function, func_args);
ret = codegen_extern_manage_return_value(ret, type);
for(size_t arg=args.size(); arg --> 0; ){
release(orig_args[arg], args[arg]->type);
if (args[arg]->on_stack)
c_SP(-1);
}
} else {
for(size_t i=0; i<args.size(); i++){
args[i]->codegen_on_stack();
}
llvm::Value* return_value_addr = type == T_VOID ? get_nullptr() : (llvm::Value*)create_alloca_in_entry(TYPE_llvm(type));
if (!ee->object_to_release)
call_addr = builder->CreateIntToPtr(getInteger(TARGET_BITS, (uint64_t)func.call), llvmType(getInt8PtrTy));
llvm::Value* args_size = getInteger(32, args.size());
llvm::Value* return_type = getInteger(TARGET_BITS, type);
builder->CreateCall4(get_global_function(func_extern_call_variant_vararg, 'v', "ppij"),
builder->CreateBitCast(return_value_addr, llvmType(getInt8PtrTy)),
call_addr,
args_size,
return_type);
if (type != T_VOID)
ret = builder->CreateLoad(return_value_addr);
}
if (on_stack)
push_value(ret, type);
return ret;
}
codegen_on_stack();
return ret_top_stack(type, on_stack);
}
void CallExpression::codegen_on_stack(){
FunctionExpression* fe = dynamic_cast<FunctionExpression*>(func);
if (fe && fe->function_kind == FUNCTION_SUBR){
//String.Mid, String.Left etc
for(size_t i=0, e=args.size(); i!=e; i++){
args[i]->codegen_on_stack();
}
void* f = (void*)klass->table[index].desc->method.exec;
builder->CreateCall(
builder->CreateBitCast(get_global(f, llvmType(getInt8Ty)), pointer_t(get_function_type('v', "h"))),
getInteger(16, args.size()));
return;
}
if (fe && fe->function_kind == FUNCTION_EVENT){
codegen_raise_event(args, ((PushEventExpression*)func)->index, on_stack);
return;
}
if (isa<PushExternExpression>(func)){
codegen_get_value();
return;
}
llvm::Value *func_value, *object;
FunctionExpression fe_temp;
if (func->type == T_CLASS){
if (desc == NULL){
//Cast
llvm::Value* ret = args[0]->codegen_get_value();
if (!args[0]->on_stack){
push_value(ret, type);
}
return;
}
func_value = get_new_struct(function_type, get_global((void*)klass, llvmType(getInt8Ty)), get_nullptr());
push_value(func_value, T_FUNCTION);
object = get_nullptr();
fe = &fe_temp;
fe->function_class = klass;
fe->function_expr_type = ClassFn;
fe->function_kind = desc->method.native ? FUNCTION_NATIVE : FUNCTION_PUBLIC;
fe->effective_class = get_global((void*)klass, llvmType(getInt8Ty));
fe->function_unknown = NULL;
} else if (fe != NULL){
func_value = func->codegen_get_value();
object = extract_value(func_value, 1);
} else {
//Variant call
func->codegen_on_stack();
}
//llvm::Value* func_value_addr = builder->CreateBitCast(get_value_on_top_addr(), pointer_t(value_types[T_FUNCTION]));
//llvm::Value* object = load_element(func_value_addr, 2);
for(size_t i=0, e=args.size(); i!=e; i++){
args[i]->codegen_on_stack();
}
//FIXME might only be needed for non-quick calls:
builder->CreateStore(getInteger(TARGET_BITS, (int64_t)(intptr_t)pc), get_global((void*)&PC, LONG_TYPE));
if (fe != NULL){
builder->CreateStore(getInteger(8, args.size()), get_global((void*)&EXEC.nparam, llvmType(getInt8Ty)));
if (fe->function_expr_type == PrivateFn){
builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
builder->CreateStore(current_op, get_global((void*)&EXEC.object));
builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass));
if (can_quick)
builder->CreateCall(get_global_function_jif(EXEC_enter_quick, 'v', ""));
else
builder->CreateCall(get_global_function_jif(EXEC_enter, 'v', ""));
if (klass->load->func[index].fast)
builder->CreateCall(get_global_function(JR_EXEC_jit_execute_function, 'v', ""));
else
builder->CreateCall(get_global_function_jif(EXEC_function_loop, 'v', ""));
} /*else if (fe->function_expr_type == UnknownFn){
*pc |= CODE_CALL_VARIANT;
} */else {
if (fe->function_unknown){
builder->CreateStore(getInteger(TARGET_BITS, (int64_t)(intptr_t)fe->function_unknown), get_global((void*)&EXEC_unknown_name, LONG_TYPE));
*pc |= CODE_CALL_VARIANT;
}
if (fe->function_kind == FUNCTION_PUBLIC){
if (can_quick)
builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), fe->effective_class, object, getInteger(32, index));
else
builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), fe->effective_class, object, getInteger(32, index));
} else if (fe->function_kind == FUNCTION_NATIVE){
builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc, llvmType(getInt8PtrTy)));
builder->CreateStore(object, get_global((void*)&EXEC.object));
builder->CreateStore(fe->effective_class, get_global((void*)&EXEC.klass));
if (can_quick)
builder->CreateCall(get_global_function_jif(EXEC_native_quick, 'v', ""));
else {
builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty)));
builder->CreateCall(get_global_function_jif(EXEC_native, 'v', ""));
}
} else if (fe->function_kind == -1){
llvm::Value* kind = extract_value(func_value, 2);
gen_if_else(builder->CreateICmpEQ(kind, getInteger(8, FUNCTION_PUBLIC)), [&](){
//Public
if (can_quick)
builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), fe->effective_class, object, getInteger(32, index));
else
builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), fe->effective_class, object, getInteger(32, index));
//builder->CreateCall(get_global_function(EXEC_jit_execute_function, 'v', ""));
}, [&](){
//Native
builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc));
builder->CreateStore(object, get_global((void*)&EXEC.object));
builder->CreateStore(fe->effective_class, get_global((void*)&EXEC.klass));
if (can_quick)
builder->CreateCall(get_global_function_jif(EXEC_native_quick, 'v', ""));
else {
builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty)));
builder->CreateCall(get_global_function_jif(EXEC_native, 'v', ""));
}
});
} else {
//FIXME hmm does this ever happen?
abort();
}
}
} else {
//Variant call
builder->CreateCall(get_global_function(JR_call, 'v', "i"), getInteger(32, args.size()));
}
if (!byref_expressions.empty()){
gen_if_noreturn(builder->CreateICmpEQ(read_global((void*)&PC, llvmType(getInt16PtrTy)), get_global((void*)pc, llvmType(getInt16Ty))), [&](){
create_throw(E_BYREF);
});
for(size_t i=0, e=byref_expressions.size(); i!=e; i++){
byref_expressions[i]->codegen();
}
}
return;
#if 0
if (desc != NULL || kind == FUNCTION_PRIVATE){
if (kind != -1){
switch(kind){
case FUNCTION_NULL: abort();
case FUNCTION_NATIVE:
builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty)));
//builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.native, llvmType(getInt8Ty)));
builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc, llvmType(getInt8PtrTy)));
builder->CreateStore(object, get_global((void*)&EXEC.object));
builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass));
if (can_quick)
builder->CreateCall(get_global_function(EXEC_native_quick, 'v', ""));
else
builder->CreateCall(get_global_function(EXEC_native, 'v', ""));
break;
case FUNCTION_PRIVATE:
//builder->CreateStore(getInteger(8, false), get_global((void*)&EXEC.native, llvmType(getInt8Ty)));
builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
builder->CreateStore(object, get_global((void*)&EXEC.object));
builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass));
if (can_quick)
builder->CreateCall(get_global_function(EXEC_enter_quick, 'v', ""));
else
builder->CreateCall(get_global_function(EXEC_enter, 'v', ""));
builder->CreateCall(get_global_function(EXEC_jit_execute_function, 'v', ""));
break;
case FUNCTION_PUBLIC: {
//builder->CreateStore(getInteger(8, false), get_global((void*)&EXEC.native, llvmType(getInt8Ty)));
/*builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc));
builder->CreateStore(object, get_global((void*)&EXEC.object));
builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass));*/
PushPureObjectExpression* ppoe = (PushPureObjectExpression*)func;
if (can_quick)
builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index));
else
builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index));
break;
}
case FUNCTION_EVENT:
builder->CreateCall3(get_global_function_vararg(GB_Raise, 'i', "pii"),
current_op, getInteger(32, index + (CP->parent ? CP->parent->n_event : 0)), getInteger(32, -args.size()));
break;
case FUNCTION_UNKNOWN:
case FUNCTION_CALL:
case FUNCTION_EXTERN:
case FUNCTION_SUBR:
abort();
}
} else {
llvm::Value* kind = extract_value(func_value, 2);
gen_if_else(builder->CreateICmpEQ(kind, getInteger(8, FUNCTION_SUBR)), [&](){
//Subr
//FIXME
}, [&](){
PushPureObjectExpression* ppoe = (PushPureObjectExpression*)func;
gen_if_else(builder->CreateICmpEQ(kind, getInteger(8, FUNCTION_PUBLIC)), [&](){
//Public
if (can_quick)
builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index));
else
builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index));
//builder->CreateCall(get_global_function(EXEC_jit_execute_function, 'v', ""));
}, [&](){
//Native
builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc));
builder->CreateStore(object, get_global((void*)&EXEC.object));
builder->CreateStore(ppoe->effective_class, get_global((void*)&EXEC.klass));
builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty)));
if (can_quick)
builder->CreateCall(get_global_function(EXEC_native_quick, 'v', ""));
else
builder->CreateCall(get_global_function(EXEC_native, 'v', ""));
});
});
}
} else {
//FIXME call variant/object runtime
}
#endif
}
llvm::Value* DupExpression::codegen_get_value(){
if (on_stack || expr->on_stack) on_stack = expr->on_stack = true;
duped->llvm_value = expr->codegen_get_value();
borrow(duped->llvm_value, type);
return duped->llvm_value;
}
void DupExpression::codegen_on_stack(){
on_stack = expr->on_stack = true;
expr->codegen_on_stack();
duped->llvm_value = ret_top_stack(type, true);
borrow(duped->llvm_value, type);
}
void OnGotoExpression::codegen(){
JumpTablePendingBranch p;
p.condition = condition->codegen_get_value();
if (condition->on_stack)
c_SP(-1);
p.insert_point = builder->GetInsertBlock(); //Must be after condition codegen!
p.destinations = &destinations;
p.default_addr = default_addr;
jump_table_pending_branches.push_back(p);
builder->SetInsertPoint(create_bb("dummy"));
}
void JumpExpression::codegen(){
PendingBranch p;
p.condition = NULL;
p.insert_point = builder->GetInsertBlock();
p.true_addr = addr;
pending_branches.push_back(p);
builder->SetInsertPoint(create_bb("dummy"));
}
void GosubExpression::codegen(){
llvm::Value* cond = NULL;
if (!destinations.empty()){
cond = condition->codegen_get_value();
if (condition->on_stack)
c_SP(-1);
}
gen_if_else(!cond ? getInteger(1, true) : builder->CreateICmpULT(cond, getInteger(32, destinations.size())), [&](){
/*llvm::Value* index = builder->CreateLoad(temp_num_gosubs_on_stack);
llvm::Value* indices[2] = {getInteger(TARGET_BITS, 0), to_target_int(index)};
builder->CreateStore(llvm::BlockAddress::get(contpoint), builder->CreateGEP(temp_gosub_stack, indices));
index = builder->CreateAdd(index, getInteger(32, 1));
builder->CreateStore(index, temp_num_gosubs_on_stack);*/
#ifndef GOSUB_ON_STACK
llvm::Value* gosub_stack_node = builder->CreateBitCast(builder->CreateCall(get_global_function(GB.Add, 'p', "p"), get_global(&GP, llvmType(getInt8Ty))), pointer_t(gosub_stack_node_type));
/*builder->CreateStore(builder->CreateLoad(gosub_return_point), create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 0));
builder->CreateStore(llvm::BlockAddress::get(contpoint), gosub_return_point);*/
unsigned int gosub_return_id = gosub_continue_points.size() + 1;
builder->CreateStore(builder->CreateLoad(gosub_return_point), create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 0));
builder->CreateStore(getInteger(16, gosub_return_id), gosub_return_point);
#else
llvm::Value* stack_addr = builder->CreateLoad(gp);
unsigned int gosub_return_id = gosub_continue_points.size() + 1;
store_value(stack_addr, builder->CreateLoad(gosub_return_point), T_SHORT);
builder->CreateStore(getInteger(16, gosub_return_id), gosub_return_point);
int diff = 1 + end_ctrl - FP->n_local;
llvm::Value* new_gp = builder->CreateGEP(stack_addr, getInteger(TARGET_BITS, diff));
gen_if_noreturn(builder->CreateICmpUGE(
builder->CreateGEP(stack_addr, getInteger(TARGET_BITS, 1 + FP->stack_usage - FP->n_local + 8)),
builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)STACK_limit), pointer_t(value_type))), [&](){
create_throw(E_STACK);
});
builder->CreateStore(new_gp, gp);
//c_SP(diff);
builder->CreateStore(new_gp, get_global(&SP, pointer_t(value_type)));
#endif
if (FP->n_ctrl){
#ifndef GOSUB_ON_STACK
llvm::Value* gp_ctrl_addr = create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 1);
builder->CreateCall2(get_global_function(GB.Alloc, 'v', "pj"), builder->CreateBitCast(gp_ctrl_addr, llvmType(getInt8PtrTy)), getInteger(TARGET_BITS, sizeof(VALUE) * FP->n_ctrl));
llvm::Value* gp_ctrl = builder->CreateLoad(gp_ctrl_addr);
#else
llvm::Value* gp_ctrl = builder->CreateGEP(stack_addr, getInteger(TARGET_BITS, 1));
#endif
for(int i=FP->n_local; i<end_ctrl; i++){
store_value(i == FP->n_local ? gp_ctrl : builder->CreateGEP(gp_ctrl, getInteger(TARGET_BITS, i - FP->n_local)), builder->CreateLoad(locals[i]), get_ctrl_type(i));
int spec_type = special_ctrl_type(get_ctrl_type(i));
if (spec_type != 0){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE)));
store_value(stack_addr, NULL, T_VOID);
}
builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - FP->n_local]);
}
for(int i=end_ctrl; i<FP->n_local+FP->n_ctrl; i++){
store_value(builder->CreateGEP(gp_ctrl, getInteger(TARGET_BITS, i - FP->n_local)), NULL, T_VOID);
llvm::Value* old_type = release_ctrl(i);
builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - FP->n_local]);
gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE)));
store_value(stack_addr, NULL, T_VOID);
}, "old_ctrl_needs_to_be_cleaned4");
}
}
}, [&](){
if (cond){
PendingBranch p;
p.condition = NULL;
p.insert_point = builder->GetInsertBlock();
p.true_addr = default_addr;
pending_branches.push_back(p);
builder->SetInsertPoint(create_bb("dummy"));
}
}, "gosub_should_run");
if (!cond){
PendingBranch p;
p.condition = NULL;
p.insert_point = builder->GetInsertBlock();
p.true_addr = gosubaddr;
pending_branches.push_back(p);
} else {
JumpTablePendingBranch p;
p.condition = cond;
p.insert_point = builder->GetInsertBlock();
p.destinations = &destinations;
p.default_addr = destinations.back();
jump_table_pending_branches.push_back(p);
}
llvm::BasicBlock* contpoint = create_bb("gosub_continue_point");
gosub_continue_points.push_back(contpoint);
builder->SetInsertPoint(contpoint);
//On return:
#ifndef GOSUB_ON_STACK
llvm::Value* gp = read_global(&GP, pointer_t(gosub_stack_node_type));
llvm::Value* gp_array = builder->CreateBitCast(gp, llvmType(getInt32PtrTy));
llvm::Value* count_addr = builder->CreateGEP(gp_array, getInteger(TARGET_BITS, -4)); //((int*)GP)[-4] == ARRAY_count(GP)
llvm::Value* index = builder->CreateAdd(builder->CreateLoad(count_addr), getInteger(32, -1));
llvm::Value* gosub_stack_node = builder->CreateGEP(gp, to_target_int(index));
builder->CreateStore(index, count_addr);
llvm::Value* old_return_point = builder->CreateLoad(create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 0));
builder->CreateStore(old_return_point, gosub_return_point);
#else
int diff = 1 + end_ctrl - FP->n_local;
llvm::Value* new_gp = builder->CreateGEP(builder->CreateLoad(gp), getInteger(TARGET_BITS, -diff));
builder->CreateStore(new_gp, gp);
//c_SP(-diff)
builder->CreateStore(new_gp, get_global(&SP, pointer_t(value_type)));
llvm::Value* old_return_point = read_value(new_gp, T_SHORT);
builder->CreateStore(old_return_point, gosub_return_point);
#endif
if (FP->n_ctrl){
#ifndef GOSUB_ON_STACK
llvm::Value* gp_ctrl_addr = create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 1);
llvm::Value* gp_ctrl = builder->CreateLoad(gp_ctrl_addr);
#else
llvm::Value* gp_ctrl = builder->CreateGEP(new_gp, getInteger(TARGET_BITS, 1));
#endif
for(int i=FP->n_local; i<end_ctrl; i++){
llvm::Value* val = read_value(i == FP->n_local ? gp_ctrl : builder->CreateGEP(gp_ctrl, getInteger(TARGET_BITS, i - FP->n_local)), get_ctrl_type(i));
builder->CreateStore(val, locals[i]);
builder->CreateStore(getInteger(32, special_ctrl_type(get_ctrl_type(i))), current_ctrl_types[i - FP->n_local]);
if (special_ctrl_type(get_ctrl_type(i)) != 0){
llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE)));
store_value(stack_addr, val, get_ctrl_type(i));
}
}
for(int i=end_ctrl; i<FP->n_local+FP->n_ctrl; i++){
builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - FP->n_local]);
}
#ifndef GOSUB_ON_STACK
builder->CreateCall(get_global_function(GB.Free, 'v', "p"), builder->CreateBitCast(gp_ctrl_addr, llvmType(getInt8PtrTy)));
#endif
}
}
void JumpIfExpression::codegen(){
PendingBranch p;
p.condition = val->codegen_get_value();
if (val->on_stack)
c_SP(-1);
p.insert_point = builder->GetInsertBlock(); //Must be after condition codegen!
p.true_addr = jump_if_true ? jump_addr : next_addr;
p.false_addr = jump_if_true ? next_addr : jump_addr;
pending_branches.push_back(p);
builder->SetInsertPoint(create_bb("dummy"));
}
void JumpFirstExpression::codegen(){
llvm::Value* to_val = to->codegen_get_value();
if (to->on_stack)
c_SP(-1);
llvm::Value* step_val = step->codegen_get_value();
if (step->on_stack)
c_SP(-1);
//builder->CreateStore(to_val, locals[ctrl_to]);
//builder->CreateStore(step_val, locals[ctrl_to+1]);
set_ctrl(to_val, to->type, ctrl_to);
set_ctrl(step_val, step->type, ctrl_to+1);
llvm::Value* ival = builder->CreateLoad(locals[local_var]);
//If step is 0, don't execute the loop
llvm::Value* condition;
if (step->type == T_SINGLE)
condition = builder->CreateFCmpUEQ(step_val, getFloat(0.0f));
else if (step->type == T_FLOAT)
condition = builder->CreateFCmpUEQ(step_val, getFloat(0.0));
else {
static const int bits[] = {0, 1, 8, 16, 32, 64};
condition = builder->CreateICmpEQ(step_val, getInteger(bits[step->type], 0));
}
gen_if_noreturn(condition, [&](){
PendingBranch p;
p.insert_point = builder->GetInsertBlock();
p.condition = NULL;
p.true_addr = done_addr;
pending_branches.push_back(p);
}, "step_is_zero", "step_not_zero");
TYPE to_type = to->type;
TYPE step_type = step->type;
//Pre-test:
bool is_float = to_type >= T_SINGLE;
llvm::Value *is_positive, *cont_neg, *cont_pos;
if (is_float){
is_positive = builder->CreateFCmpUGE(step_val, to_type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0));
cont_neg = builder->CreateFCmpUGE(ival, to_val);
cont_pos = builder->CreateFCmpULE(ival, to_val);
} else {
static const int bits[] = {0, 1, 8, 16, 32, 64};
is_positive = builder->CreateICmpSGE(step_val, getInteger(bits[step_type], 0));
if (to_type == T_BYTE){
cont_neg = builder->CreateICmpUGE(ival, to_val);
cont_pos = builder->CreateICmpULE(ival, to_val);
} else {
cont_neg = builder->CreateICmpSGE(ival, to_val);
cont_pos = builder->CreateICmpSLE(ival, to_val);
}
}
llvm::Value* cont = builder->CreateSelect(is_positive, cont_pos, cont_neg);
PendingBranch p;
p.insert_point = builder->GetInsertBlock();
p.condition = cont;
p.true_addr = body_addr;
p.false_addr = done_addr;
pending_branches.push_back(p);
builder->SetInsertPoint(create_bb("dummy"));
}
void JumpNextExpression::codegen(){
llvm::Value* to_val = builder->CreateLoad(locals[ctrl_to]);
llvm::Value* step_val = builder->CreateLoad(locals[ctrl_to+1]);
llvm::Value* ival = builder->CreateLoad(locals[local_var]);
TYPE to_type = get_ctrl_type(ctrl_to);
TYPE step_type = get_ctrl_type(ctrl_to+1);
//Add step
if (step_type != to_type){
if (to_type == T_BYTE)
ival = builder->CreateZExt(ival, llvmType(getInt32Ty));
else
ival = builder->CreateSExt(ival, llvmType(getInt32Ty));
ival = builder->CreateAdd(ival, step_val, "", false, true /*nsw*/);
ival = builder->CreateTrunc(ival, to_val->getType());
builder->CreateStore(ival, locals[local_var]);
} else {
if (to_type == T_SINGLE || to_type == T_FLOAT)
ival = builder->CreateFAdd(ival, step_val);
else
ival = builder->CreateAdd(ival, step_val, "", false, true /*nsw*/);
builder->CreateStore(ival, locals[local_var]);
}
//Test if continue
bool is_float = to_type >= T_SINGLE;
llvm::Value *is_positive, *cont_neg, *cont_pos;
if (is_float){
is_positive = builder->CreateFCmpUGE(step_val, to_type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0));
cont_neg = builder->CreateFCmpUGE(ival, to_val);
cont_pos = builder->CreateFCmpULE(ival, to_val);
} else {
static const int bits[] = {0, 1, 8, 16, 32, 64};
is_positive = builder->CreateICmpSGE(step_val, getInteger(bits[step_type], 0));
if (to_type == T_BYTE){
cont_neg = builder->CreateICmpUGE(ival, to_val);
cont_pos = builder->CreateICmpULE(ival, to_val);
} else {
cont_neg = builder->CreateICmpSGE(ival, to_val);
cont_pos = builder->CreateICmpSLE(ival, to_val);
}
}
llvm::Value* cont = builder->CreateSelect(is_positive, cont_pos, cont_neg);
PendingBranch p;
p.insert_point = builder->GetInsertBlock();
p.condition = cont;
p.true_addr = body_addr;
p.false_addr = done_addr;
pending_branches.push_back(p);
builder->SetInsertPoint(create_bb("dummy"));
}
void JumpEnumFirstExpression::codegen(){
//FIXME fix class enum
if (!TYPE_is_pure_object(obj->type)){
codegen_pop_ctrl(obj, ctrl);
builder->CreateCall(get_global_function_jif(EXEC_enum_first, 'v', "h"), getInteger(16, ctrl));
set_ctrl_type(T_OBJECT, ctrl+1);
} else {
llvm::Value* object = obj->codegen_get_value();
ob = extract_value(object, 1);
codegen_pop_ctrl(object, obj, ctrl);
/* Instance variables:
llvm::Value* effective_class;
llvm::Value* obj;
*/
llvm::Value* enum_object;
CLASS* klass = (CLASS*)(void*)obj->type;
if (klass->is_virtual){
effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, obj->type), llvmType(getInt8PtrTy));
//Ok, so what we should pass to CENUM_create is the pointer to the class (in case of a virtual class is enumerated), or
//pointer to the object (in case of an instance of a virtual class is enumerated).
//As val._class.class is the same memory as val._object.object, we don't need to do anything special here.
enum_object = ob;
//However, when running EXEC_special with SPEC_FIRST, we need to pass null as object (if virtual class), so we replace ob with null
ob = builder->CreateSelect(builder->CreateICmpEQ(builder->CreatePtrToInt(extract_value(object, 0), llvmType(getInt32Ty)), getInteger(32, T_CLASS)), get_nullptr(), ob);
//old: enum_object = builder->CreateSelect(builder->CreateICmpEQ(ob, get_nullptr()), effective_class, ob);
} else {
effective_class = extract_value(object, 0);
make_nullcheck(ob);
enum_object = ob;
}
llvm::Value* cenum_obj = builder->CreateCall(get_global_function_jif(CENUM_create, 'p', "p"), enum_object);
//unref_object(extract_value(builder->CreateLoad(locals[ctrl+1]), 1));
borrow_object_no_nullcheck(cenum_obj);
llvm::Value* cenum_object = get_new_struct(object_type,
get_global((void*)GB.FindClass("Enum"), llvmType(getInt8Ty)), cenum_obj);
set_ctrl(cenum_object, T_OBJECT, ctrl+1);
builder->CreateStore(read_global((void*)&EXEC_enum), temp_voidptr);
builder->CreateStore(cenum_obj, get_global((void*)&EXEC_enum));
builder->CreateCall5(get_global_function_jif(EXEC_special, 'c', "ippic"),
getInteger(32, SPEC_FIRST),
effective_class,
ob,
getInteger(32, 0),
getInteger(8, true));
builder->CreateStore(builder->CreateLoad(temp_voidptr), get_global((void*)&EXEC_enum));
}
}
llvm::Value* OnStackExpression::codegen_get_value(){
return get_top(type);
}
void JumpEnumNextExpression::codegen(){
//FIXME class enum
llvm::Value* stop;
if (!TYPE_is_pure_object(jfirst->obj->type)){
store_pc(pc);
stop = builder->CreateICmpNE(builder->CreateCall(get_global_function_jif(EXEC_enum_next, 'c', "h"), getInteger(16, drop)), getInteger(8, false));
} else {
llvm::Value* cenum_obj = extract_value(builder->CreateLoad(locals[jfirst->ctrl+1]), 1);
int stop_offset = sizeof(GB_BASE) + sizeof(LIST) + 5*sizeof(void*);
stop = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(cenum_obj, getInteger(TARGET_BITS, stop_offset))), llvmType(getInt1Ty));
stop = gen_if_phi(stop, builder->CreateXor(stop, getInteger(1, 1)), [&](){
builder->CreateStore(read_global((void*)&EXEC_enum), temp_voidptr);
builder->CreateStore(cenum_obj, get_global((void*)&EXEC_enum));
store_pc(pc); //Used by EXEC_native
llvm::Value* err = builder->CreateCall5(get_global_function_jif(EXEC_special, 'c', "ippic"),
getInteger(32, SPEC_NEXT),
jfirst->effective_class,
jfirst->ob,
getInteger(32, 0),
getInteger(8, false));
builder->CreateStore(builder->CreateLoad(temp_voidptr), get_global((void*)&EXEC_enum));
gen_if_noreturn(builder->CreateICmpNE(err, getInteger(8, false)), [&](){
create_throw(E_ENUM);
});
llvm::Value* stop_after = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(cenum_obj, getInteger(TARGET_BITS, stop_offset))), llvmType(getInt1Ty));
gen_if(builder->CreateOr(getInteger(1, drop), stop_after), [&](){
//When enumeration stops, T_VOID can be returned instead of the given type
llvm::Value* t = get_top(type);
llvm::Value* tt = load_element(get_value_on_top_addr(), 0);
if (TYPE_is_object(type))
tt = builder->CreatePtrToInt(tt, LONG_TYPE);
gen_if(builder->CreateICmpNE(tt, getInteger(TARGET_BITS, T_VOID)), [&](){
release(t, type);
});
c_SP(-1);
});
return stop_after;
}, "enum_next_cont");
}
PendingBranch p;
p.condition = stop;
p.insert_point = builder->GetInsertBlock();
p.true_addr = addr;
p.false_addr = cont_addr;
pending_branches.push_back(p);
builder->SetInsertPoint(create_bb("dummy"));
}
void TryExpression::codegen(){
in_try = true;
has_tries = true;
try_blocks.push_back(builder->GetInsertBlock());
builder->CreateStore(read_sp(), get_global((void*)&EP, pointer_t(value_type)));
//We only want it to be non-null, so let's put in 1:
builder->CreateStore(get_global((void*)1, llvmType(getInt8Ty)), get_global((void*)&EC));
llvm::Value* jmpbuf = builder->CreateCall(get_global_function(JR_try, 'p', "p"),
create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, 0));
llvm::Function* f = llvm::cast<llvm::Function>(get_global_function(_setjmp, 'i', "p"));
#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 2)
f->addFnAttr(llvm::Attributes::ReturnsTwice);
#else
f->addFnAttr(llvm::Attribute::ReturnsTwice);
#endif
llvm::Value* setjmp_return = builder->CreateCall(f, jmpbuf);
builder->CreateStore(builder->CreateTrunc(setjmp_return, llvmType(getInt8Ty)),
create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, offsetof(ERROR_CONTEXT, ret)));
llvm::Value* second_time = builder->CreateICmpNE(setjmp_return, getInteger(32, 0));
gen_if_else(second_time, [&](){
builder->CreateCall(get_global_function(JR_try_unwind, 'v', "p"), builder->CreateBitCast(builder->CreateLoad(gp), llvmType(getInt8PtrTy)));
builder->CreateStore(getInteger(1, true), temp_got_error);
}, [&](){
builder->CreateStore(getInteger(1, false), temp_got_error);
}, "Try_cleanup");
PendingBranch p;
p.condition = second_time;
p.insert_point = builder->GetInsertBlock();
p.true_addr = addr2;
p.false_addr = addr1;
pending_branches.push_back(p);
builder->SetInsertPoint(create_bb("dummy"));
}
void EndTryExpression::codegen(){
in_try = false;
llvm::Value* call = builder->CreateCall(get_global_function(JR_end_try, 'v', "p"),
create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, 0));
if (llvm::Instruction* inst = llvm::dyn_cast<llvm::Instruction>(call)){
llvm::Value* arr[1] = {getInteger(32, 1)};
inst->setMetadata("end_try", llvm::MDNode::get(llvm_context, arr));
}
builder->CreateStore(get_nullptr(), get_global((void*)&EP));
//We only need to store NULL (no catch) or something whatever else inside EC (there is a catch)
llvm::Value* got_large_error = builder->CreateZExt(builder->CreateXor(builder->CreateLoad(temp_got_error2), getInteger(1, 1)), LONG_TYPE);
builder->CreateStore(got_large_error, get_global((void*)&EC, LONG_TYPE));
}
void LargeTryExpression::codegen(){
has_tries = true;
builder->CreateStore(getInteger(1, false), temp_got_error2);
llvm::Value* jmpbuf = builder->CreateCall(get_global_function(JR_try, 'p', "p"),
create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0));
llvm::Function* f = llvm::cast<llvm::Function>(get_global_function(_setjmp, 'i', "p"));
#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 2)
f->addFnAttr(llvm::Attributes::ReturnsTwice);
#else
f->addFnAttr(llvm::Attribute::ReturnsTwice);
#endif
llvm::Value* setjmp_return = builder->CreateCall(f, jmpbuf);
builder->CreateStore(builder->CreateTrunc(setjmp_return, llvmType(getInt8Ty)),
create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, offsetof(ERROR_CONTEXT, ret)));
llvm::Value* second_time = builder->CreateICmpNE(setjmp_return, getInteger(32, 0));
gen_if(second_time, [&](){
builder->CreateCall(get_global_function(JR_try_unwind, 'v', "p"), builder->CreateBitCast(builder->CreateLoad(gp), llvmType(getInt8PtrTy)));
builder->CreateCall(get_global_function(JR_end_try, 'v', "p"),
create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0));
builder->CreateStore(get_nullptr(), get_global((void*)&EC));
builder->CreateStore(getInteger(1, true), temp_got_error2);
}, "Try_cleanup");
PendingBranch p;
p.condition = second_time;
p.insert_point = builder->GetInsertBlock();
p.true_addr = addr2;
p.false_addr = 0;
pending_branches.push_back(p);
builder->SetInsertPoint(create_bb("dummy"));
}
void LargeCatchExpression::codegen(){
//Also in ReturnExpression if returning before this
builder->CreateCall(get_global_function(JR_end_try, 'v', "p"),
create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0));
//Differs from original interpreter, error thrown in a Finally block is not catched.
builder->CreateStore(get_nullptr(), get_global((void*)&EC));
}
void CatchExpression::codegen(){
llvm::Value* got_error = builder->CreateLoad(temp_got_error2);
gen_if_noreturn(builder->CreateXor(got_error, getInteger(1, 1)), [&](){
store_value(get_global((void*)RP), get_default(FP->type), FP->type);
return_points.push_back(builder->GetInsertBlock());
}, "do_not_catch", "do_catch");
}
llvm::Value* NewExpression::codegen_get_value(){
codegen_on_stack();
return ret_top_stack(type, true);
}
void NewExpression::codegen_on_stack(){
for(size_t i=0, e=args.size(); i!=e; i++){
args[i]->codegen_on_stack();
}
store_pc(pc);
builder->CreateCall(get_global_function_jif(EXEC_new, 'v', ""));
}
std::pair<llvm::Value*, llvm::Value*> BinOpExpression::codegen_operands(){
llvm::Value* l = left->codegen_get_value();
if (left->on_stack)
c_SP(-1);
llvm::Value* r = right->codegen_get_value();
if (right->on_stack)
c_SP(-1);
return std::make_pair(l, r);
}
llvm::Value* EqExpression::codegen_get_value(){
llvm::Value* ret;
if ((t >= T_BOOLEAN && t <= T_LONG) || t == T_POINTER){
auto op = codegen_operands();
ret = builder->CreateICmpEQ(op.first, op.second);
} else if (t == T_DATE){
auto op = codegen_operands();
ret = builder->CreateAnd(
builder->CreateICmpEQ(extract_value(op.first, 0), extract_value(op.second, 0)),
builder->CreateICmpEQ(extract_value(op.first, 1), extract_value(op.second, 1))
);
} else if (t == T_NULL){
//FIXME gör några unit tests på den här biten
if (left->type == T_NULL && right->type == T_NULL){
ret = getInteger(1, true);
} else {
Expression* testexpr = left->type == T_NULL ? right : left;
llvm::Value* val = testexpr->codegen_get_value();
switch(testexpr->type){
case T_STRING:
case T_CSTRING:
ret = builder->CreateICmpEQ(extract_value(val, 3), getInteger(32, 0));
if (testexpr->type == T_STRING)
release(val, T_STRING);
break;
case T_DATE:
ret = builder->CreateICmpEQ(builder->CreateOr(extract_value(val, 0), extract_value(val, 1)), getInteger(32, 0));
break;
case T_POINTER:
ret = builder->CreateICmpEQ(val, get_nullptr());
break;
case T_VARIANT: {
llvm::Value* vtype = extract_value(val, 0);
ret = gen_if_phi(getInteger(1, true), builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_NULL)), [&](){
if (TARGET_BITS == 64){
llvm::Value* date_str_ptr_obj = builder->CreateOr(
builder->CreateAnd(
builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_DATE)),
builder->CreateICmpULE(vtype, getInteger(TARGET_BITS, T_POINTER))
),
builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT))
);
llvm::Value* zero_data = builder->CreateICmpEQ(extract_value(val, 1), getInteger(64, 0));
return builder->CreateSelect(date_str_ptr_obj, zero_data, getInteger(1, false));
} else {
llvm::Value* vdata = extract_value(val, 1);
llvm::Value* ptr_is_null = builder->CreateICmpEQ(builder->CreateTrunc(vdata, llvmType(getInt32Ty)), getInteger(32, 0));
return (llvm::Value*)gen_if_phi(ptr_is_null, builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_STRING)), [&](){
llvm::Value* zero_date = builder->CreateICmpEQ(vdata, getInteger(64, 0));
return gen_if_phi(zero_date, builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_DATE)), [&](){
return builder->CreateSelect(
builder->CreateOr(
builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_POINTER)),
builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT))
),
ptr_is_null, getInteger(1, false)
);
});
});
}
});
gen_if(builder->CreateXor(ret, getInteger(1, 1)), [&](){
release(val, T_VARIANT);
});
break;
}
default:
if (TYPE_is_object(testexpr->type)){
llvm::Value* object = extract_value(val, 1);
ret = builder->CreateICmpEQ(object, get_nullptr());
unref_object(object);
} else {
ret = getInteger(1, false);
}
break;
}
if (left->on_stack)
c_SP(-1);
if (right->on_stack)
c_SP(-1);
}
} else if (t == T_STRING || t == T_CSTRING){
auto op = codegen_operands();
llvm::Value* len1 = extract_value(op.first, 3);
llvm::Value* len2 = extract_value(op.second, 3);
llvm::Value *ret1, *ret2;
llvm::BasicBlock *BB1, *BB2;
gen_if_else(builder->CreateICmpNE(len1, len2), [&](){
ret1 = getInteger(1, false);
BB1 = builder->GetInsertBlock();
}, [&](){
llvm::Value* addr1 = extract_value(op.first, 1);
llvm::Value* addr2 = extract_value(op.second, 1);
llvm::Value* offset1 = extract_value(op.first, 2);
llvm::Value* offset2 = extract_value(op.second, 2);
if (TARGET_BITS == 64){
offset1 = builder->CreateZExt(offset1, llvmType(getInt64Ty));
offset2 = builder->CreateZExt(offset2, llvmType(getInt64Ty));
len1 = builder->CreateZExt(len1, llvmType(getInt64Ty));
}
addr1 = builder->CreateGEP(addr1, offset1);
addr2 = builder->CreateGEP(addr2, offset2);
ret2 = builder->CreateICmpEQ(builder->CreateCall3(get_global_function(memcmp, 'i', "ppj"), addr1, addr2, len1), getInteger(32, 0));
BB2 = builder->GetInsertBlock();
}, "strcomp_not_same_length", "strcomp_same_length", "strcomp_done");
llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2);
phi->addIncoming(ret1, BB1);
phi->addIncoming(ret2, BB2);
ret = phi;
release(op.first, left->type);
release(op.second, right->type);
} else if (t == T_SINGLE || t == T_FLOAT){
auto op = codegen_operands();
ret = builder->CreateFCmpUEQ(op.first, op.second);
} else if (t == T_OBJECT){
llvm::Value* l = left->codegen_get_value();
llvm::Value* r = right->codegen_get_value();
llvm::Value* sp = read_sp();//builder->CreateBitCast(read_sp(), pointer_t(value_types[T_OBJECT]));
llvm::Value* laddr = builder->CreateGEP(sp, getInteger(TARGET_BITS, -2));//create_gep(sp, TARGET_BITS, -2, 32, 1);
llvm::Value* raddr = builder->CreateGEP(sp, getInteger(TARGET_BITS, -1));//create_gep(sp, TARGET_BITS, -1, 32, 1);
laddr = create_gep(builder->CreateBitCast(laddr, pointer_t(value_types[T_OBJECT])), TARGET_BITS, 0, 32, 1);
raddr = create_gep(builder->CreateBitCast(raddr, pointer_t(value_types[T_OBJECT])), TARGET_BITS, 0, 32, 1);
llvm::Value* ret = builder->CreateCall2(get_global_function_jif(COMPARE_object, 'i', "pp"),
builder->CreateBitCast(laddr, llvmType(getInt8PtrTy)),
builder->CreateBitCast(raddr, llvmType(getInt8PtrTy)));
ret = builder->CreateICmpEQ(ret, getInteger(32, 0));
unref_object(extract_value(l, 1));
unref_object(extract_value(r, 1));
c_SP(-2);
if (on_stack)
push_value(ret, T_BOOLEAN);
return ret;
} else if (t == T_VARIANT){
left->codegen_on_stack();
right->codegen_on_stack();
builder->CreateCall(get_global_function(JR_variant_equal, 'v', ""));
return ret_top_stack(T_BOOLEAN, on_stack);
}
if (on_stack)
push_value(ret, T_BOOLEAN);
return ret;
}
static llvm::Value* is_empty_string(llvm::Value* str){
//Null test of address should not be needed. Length check should be enough...
llvm::Value* len = extract_value(str, 3);
return builder->CreateICmpEQ(len, getInteger(32, 0));
}
llvm::Value* NotExpression::codegen_get_value(){
if (expr->type == T_NULL){
if (on_stack)
push_value(getInteger(1, true), T_BOOLEAN);
return getInteger(1, true);
} else if (TYPE_is_variant(expr->type)){
expr->codegen_on_stack();
builder->CreateCall(get_global_function_jif(SUBR_not, 'v', "h"), getInteger(16, 0/*T_VARIANT*/));
return ret_top_stack(T_VARIANT, true);
} else {
llvm::Value* op = expr->codegen_get_value();
if (expr->on_stack)
c_SP(-1);
llvm::Value* ret;
if (expr->type <= T_LONG){
static const int bits[] = {0, 1, 8, 16, 32, 64};
ret = builder->CreateXor(op, getInteger(bits[expr->type], -1));
} else if (TYPE_is_string(expr->type)){
ret = is_empty_string(op);
release(op, expr->type);
} else if (TYPE_is_object(expr->type)){
ret = builder->CreateICmpEQ(extract_value(op, 1), get_nullptr());
release(op, expr->type);
}
if (on_stack)
push_value(ret, T_BOOLEAN);
return ret;
}
}
static llvm::Value* LessDate(llvm::Value* date_left, llvm::Value* date_right){
llvm::Value* d1 = extract_value(date_left, 0);
llvm::Value* d2 = extract_value(date_right, 0);
llvm::Value* t1 = extract_value(date_left, 1);
llvm::Value* t2 = extract_value(date_right, 1);
llvm::BasicBlock *BB1, *BB2, *BB3, *BB4;
llvm::Value* r3;
gen_if_else(builder->CreateICmpSLT(d1, d2), [&](){
BB1 = builder->GetInsertBlock(); //true
}, [&](){
gen_if_else(builder->CreateICmpSGT(d1, d2), [&](){
BB2 = builder->GetInsertBlock(); //false
}, [&](){
r3 = builder->CreateICmpSLT(t1, t2);
BB3 = builder->GetInsertBlock(); //r3
});
llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2);
phi->addIncoming(getInteger(1, false), BB2);
phi->addIncoming(r3, BB3);
r3 = phi;
BB4 = builder->GetInsertBlock();
});
llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2);
phi->addIncoming(getInteger(1, true), BB1);
phi->addIncoming(r3, BB4);
return phi;
}
llvm::Value* LessExpression::codegen_get_value(){
if (t == T_VARIANT){
left->codegen_on_stack();
right->codegen_on_stack();
builder->CreateCall(get_global_function(JR_variant_compi_less_than, 'v', ""));
return ret_top_stack(T_BOOLEAN, true);
} else {
auto op = codegen_operands();
llvm::Value* ret;
if (t <= T_LONG || t == T_POINTER){
ret = builder->CreateICmpSLT(op.first, op.second);
} else if (t == T_DATE){
ret = LessDate(op.first, op.second);
} else if (t == T_SINGLE || t == T_FLOAT){
ret = builder->CreateFCmpULT(op.first, op.second);
} /*else if (TYPE_is_object(t)){
//FIXME the interpreter does not allow this ..
abort();
} */else if (TYPE_is_string(t)){
llvm::Value* addr1 = extract_value(op.first, 1);
llvm::Value* addr2 = extract_value(op.second, 1);
llvm::Value* offset1 = extract_value(op.first, 2);
llvm::Value* offset2 = extract_value(op.second, 2);
llvm::Value* len1 = extract_value(op.first, 3);
llvm::Value* len2 = extract_value(op.second, 3);
if (TARGET_BITS == 64){
offset1 = builder->CreateZExt(offset1, llvmType(getInt64Ty));
offset2 = builder->CreateZExt(offset2, llvmType(getInt64Ty));
}
addr1 = builder->CreateGEP(addr1, offset1);
addr2 = builder->CreateGEP(addr2, offset2);
ret = builder->CreateICmpEQ(builder->CreateCall4(get_global_function_jif(STRING_compare, 'i', "pipi"), addr1, len1, addr2, len2), getInteger(32, -1));
release(op.first, left->type);
release(op.second, right->type);
}
if (on_stack)
push_value(ret, T_BOOLEAN);
return ret;
}
}
llvm::Value* NearExpression::codegen_get_value(){
auto op = codegen_operands();
llvm::Value* ret;
llvm::Value* len1 = extract_value(op.first, 3);
llvm::Value* len2 = extract_value(op.second, 3);
llvm::Value *ret1, *ret2;
llvm::BasicBlock *BB1, *BB2;
gen_if_else(builder->CreateICmpNE(len1, len2), [&](){
ret1 = getInteger(1, false);
BB1 = builder->GetInsertBlock();
}, [&](){
llvm::Value* addr1 = extract_value(op.first, 1);
llvm::Value* addr2 = extract_value(op.second, 1);
llvm::Value* offset1 = extract_value(op.first, 2);
llvm::Value* offset2 = extract_value(op.second, 2);
if (TARGET_BITS == 64){
offset1 = builder->CreateZExt(offset1, llvmType(getInt64Ty));
offset2 = builder->CreateZExt(offset2, llvmType(getInt64Ty));
}
addr1 = builder->CreateGEP(addr1, offset1);
addr2 = builder->CreateGEP(addr2, offset2);
ret2 = builder->CreateICmpNE(builder->CreateCall3(get_global_function_jif(STRING_equal_ignore_case_same, 'c', "ppi"), addr1, addr2, len1), getInteger(8, 0));
BB2 = builder->GetInsertBlock();
}, "strcomp_not_same_length", "strcomp_same_length", "strcomp_done");
llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2);
phi->addIncoming(ret1, BB1);
phi->addIncoming(ret2, BB2);
ret = phi;
release(op.first, left->type);
release(op.second, right->type);
if (on_stack)
push_value(ret, T_BOOLEAN);
return ret;
}
llvm::Value* AddExpression::codegen_get_value(){
if (type == T_VARIANT){
left->codegen_on_stack();
right->codegen_on_stack();
builder->CreateCall(get_global_function(JR_add, 'v', "h"), getInteger(16, 0));
return ret_top_stack(T_VARIANT, true);
} else {
auto op = codegen_operands();
llvm::Value* ret;
if (type == T_BOOLEAN){
ret = builder->CreateOr(op.first, op.second);
} else if (type <= T_LONG || type == T_POINTER){
ret = builder->CreateAdd(op.first, op.second);
} else /*if (type <= T_FLOAT)*/{
ret = builder->CreateFAdd(op.first, op.second);
}
if (on_stack)
push_value(ret, type);
return ret;
}
}
llvm::Value* SubExpression::codegen_get_value(){
if (type == T_VARIANT){
left->codegen_on_stack();
right->codegen_on_stack();
builder->CreateCall(get_global_function(JR_sub, 'v', "h"), getInteger(16, 0));
return ret_top_stack(T_VARIANT, true);
} else {
auto op = codegen_operands();
llvm::Value* ret;
if (type == T_BOOLEAN){
ret = builder->CreateXor(op.first, op.second);
} else if (type <= T_LONG || type == T_POINTER){
ret = builder->CreateSub(op.first, op.second);
} else /*if (type <= T_FLOAT)*/{
ret = builder->CreateFSub(op.first, op.second);
}
if (on_stack)
push_value(ret, type);
return ret;
}
}
llvm::Value* MulExpression::codegen_get_value(){
if (type == T_VARIANT){
left->codegen_on_stack();
right->codegen_on_stack();
builder->CreateCall(get_global_function(JR_mul, 'v', "h"), getInteger(16, 0));
return ret_top_stack(T_VARIANT, true);
} else {
auto op = codegen_operands();
llvm::Value* ret;
if (type == T_BOOLEAN){
ret = builder->CreateAnd(op.first, op.second);
} else if (type <= T_LONG || type == T_POINTER){
ret = builder->CreateMul(op.first, op.second);
} else /*if (type <= T_FLOAT)*/{
ret = builder->CreateFMul(op.first, op.second);
}
if (on_stack)
push_value(ret, type);
return ret;
}
}
llvm::Value* DivExpression::codegen_get_value(){
auto op = codegen_operands();
gen_if_noreturn(builder->CreateFCmpUEQ(op.second, getFloat(0.0)), [&](){
create_throw(E_ZERO);
}, "div_zero", "not_div_zero");
llvm::Value* ret = builder->CreateFDiv(op.first, op.second);
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* QuoExpression::codegen_get_value(){
auto op = codegen_operands();
llvm::Value* ret;
if (type == T_BOOLEAN){
gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(1, false)), [&](){
create_throw(E_ZERO);
}, "div_zero", "not_div_zero");
ret = op.first;
} else {
static const int bits[] = {0, 1, 8, 16, 32, 64};
gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(bits[type], 0)), [&](){
create_throw(E_ZERO);
}, "div_zero", "not_div_zero");
if (type == T_BYTE)
ret = builder->CreateUDiv(op.first, op.second);
else
ret = builder->CreateSDiv(op.first, op.second);
}
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* RemExpression::codegen_get_value(){
auto op = codegen_operands();
llvm::Value* ret;
if (type == T_BOOLEAN){
gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(1, false)), [&](){
create_throw(E_ZERO);
}, "div_zero", "not_div_zero");
ret = op.first;
} else {
static const int bits[] = {0, 1, 8, 16, 32, 64};
gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(bits[type], 0)), [&](){
create_throw(E_ZERO);
}, "div_zero", "not_div_zero");
if (type == T_BYTE)
ret = builder->CreateURem(op.first, op.second);
else
ret = builder->CreateSRem(op.first, op.second);
}
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* PowExpression::codegen_get_value(){
auto op = codegen_operands();
llvm::Value* func;
if (right->type == T_INTEGER){
llvm::Type* types[] = {llvmType(getDoubleTy)};
func = llvm::Intrinsic::getDeclaration(M, llvm::Intrinsic::powi, types);
//func = get_global_function(__powidf2, 'd', "di");
//func = M->getOrInsertFunction("llvm.powi.f64", get_function_type('d', "di"));
} else {
func = M->getOrInsertFunction("llvm.pow.f64", get_function_type('d', "dd"));
}
llvm::Value* ret = builder->CreateCall2(func, op.first, op.second);
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* AndExpression::codegen_get_value(){
if (type == T_VARIANT){
left->codegen_on_stack();
right->codegen_on_stack();
builder->CreateCall(get_global_function_jif(SUBR_and_, 'v', "h"), getInteger(16, C_AND));
return ret_top_stack(T_VARIANT, on_stack);
} else {
auto op = codegen_operands();
llvm::Value* ret = builder->CreateAnd(op.first, op.second);
if (on_stack)
push_value(ret, type);
return ret;
}
}
llvm::Value* OrExpression::codegen_get_value(){
if (type == T_VARIANT){
left->codegen_on_stack();
right->codegen_on_stack();
builder->CreateCall(get_global_function_jif(SUBR_and_, 'v', "h"), getInteger(16, C_OR));
return ret_top_stack(T_VARIANT, on_stack);
} else {
auto op = codegen_operands();
llvm::Value* ret = builder->CreateOr(op.first, op.second);
if (on_stack)
push_value(ret, type);
return ret;
}
}
llvm::Value* XorExpression::codegen_get_value(){
if (type == T_VARIANT){
left->codegen_on_stack();
right->codegen_on_stack();
builder->CreateCall(get_global_function_jif(SUBR_and_, 'v', "h"), getInteger(16, C_XOR));
return ret_top_stack(T_VARIANT, on_stack);
} else {
auto op = codegen_operands();
llvm::Value* ret = builder->CreateXor(op.first, op.second);
if (on_stack)
push_value(ret, type);
return ret;
}
}
llvm::Value* IsExpression::codegen_get_value(){
llvm::Value* obj = left->codegen_get_value();
if (left->on_stack)
c_SP(-1);
llvm::Value* addr = extract_value(obj, 1);
llvm::BasicBlock *BB1, *BB2, *BB3, *BB4;
llvm::Value* ret;
BB1 = builder->GetInsertBlock(); //false
gen_if(builder->CreateICmpNE(addr, get_nullptr()), [&](){
llvm::Value* OBJ = builder->CreateBitCast(addr, pointer_t(OBJECT_type));
llvm::Value* obj_klass = load_element(OBJ, 0);
PushClassExpression* pce = dyn_cast<PushClassExpression>(right);
assert(pce);
gen_if_else(builder->CreateICmpEQ(builder->CreateIntToPtr(getInteger(TARGET_BITS, (intptr_t)(void*)pce->klass), llvmType(getInt8PtrTy)), obj_klass), [&](){
BB2 = builder->GetInsertBlock(); //true
}, [&](){
ret = builder->CreateCall2(get_global_function_jif(CLASS_inherits, 'c', "pp"), obj_klass, builder->CreateIntToPtr(getInteger(TARGET_BITS, (intptr_t)(void*)pce->klass), llvmType(getInt8PtrTy)));
ret = builder->CreateICmpNE(ret, getInteger(8, false));
BB3 = builder->GetInsertBlock();
});
llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2);
phi->addIncoming(getInteger(1, true), BB2);
phi->addIncoming(ret, BB3);
ret = phi;
unref_object(addr);
BB4 = builder->GetInsertBlock();
});
llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2);
phi->addIncoming(getInteger(1, false), BB1);
phi->addIncoming(ret, BB4);
ret = phi;
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* NegExpression::codegen_get_value(){
if (type == T_VARIANT){
llvm::Value* val = expr->codegen_get_value();
c_SP(-expr->on_stack);
llvm::Value* vtype = extract_value(val, 0);
llvm::Value* data = extract_value(val, 1);
llvm::BasicBlock* bb[8] = {
create_bb("else"),
create_bb("bool"),
create_bb("byte"),
create_bb("short"),
create_bb("integer"),
create_bb("long"),
create_bb("single"),
create_bb("float")
};
llvm::BasicBlock* done_block = create_bb("done_neg");
llvm::SwitchInst* si = builder->CreateSwitch(vtype, bb[0], 7);
for(int i=1; i<8; i++)
si->addCase(getInteger(TARGET_BITS, i), bb[i]);
llvm::Value* res[8];
builder->SetInsertPoint(bb[1]); //bool
res[1] = data;
builder->CreateBr(done_block);
builder->SetInsertPoint(bb[2]); //byte
res[2] = builder->CreateZExt(
builder->CreateSub(getInteger(8, 0), builder->CreateTrunc(data, llvmType(getInt8Ty))),
llvmType(getInt64Ty)
);
builder->CreateBr(done_block);
builder->SetInsertPoint(bb[3]); //short
res[3] = builder->CreateSExt(
builder->CreateSub(getInteger(16, 0), builder->CreateTrunc(data, llvmType(getInt16Ty))),
llvmType(getInt64Ty)
);
builder->CreateBr(done_block);
builder->SetInsertPoint(bb[4]); //integer
res[4] = builder->CreateSExt(
builder->CreateSub(getInteger(32, 0), builder->CreateTrunc(data, llvmType(getInt32Ty))),
llvmType(getInt64Ty)
);
builder->CreateBr(done_block);
builder->SetInsertPoint(bb[5]); //long
res[5] = builder->CreateSub(getInteger(64, 0), data);
builder->CreateBr(done_block);
/* %1 = trunc i64 %data to i32
%2 = bitcast i32 %1 to float
%3 = fsub float -0.000000e+00, %2
%4 = bitcast float %3 to i32
%5 = zext i32 %4 to i64
ret i64 %5 */
builder->SetInsertPoint(bb[6]); //single
res[6] = builder->CreateFSub(getFloat(0.0f), builder->CreateBitCast(builder->CreateTrunc(data, llvmType(getInt32Ty)), llvmType(getFloatTy)));
res[6] = builder->CreateZExt(builder->CreateBitCast(res[6], llvmType(getInt32Ty)), llvmType(getInt64Ty));
builder->CreateBr(done_block);
builder->SetInsertPoint(bb[7]); //float
res[7] = builder->CreateBitCast(builder->CreateFSub(getFloat(0.0), builder->CreateBitCast(data, llvmType(getDoubleTy))), llvmType(getInt64Ty));
builder->CreateBr(done_block);
builder->SetInsertPoint(bb[0]); //else
release(val, T_VARIANT);
create_throw(E_TYPE, "Number", "(unknown)");
builder->SetInsertPoint(done_block);
llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt64Ty), 7, "variant_neg_result");
for(int i=1; i<8; i++){
phi->addIncoming(res[i], bb[i]);
}
llvm::Value* ret = get_new_struct(variant_type, vtype, phi);
if (on_stack)
push_value(ret, type);
return ret;
} else {
llvm::Value* val = expr->codegen_get_value();
if (expr->on_stack)
c_SP(-1);
llvm::Value* ret;
if (type == T_BOOLEAN){
ret = val;
} else if (type <= T_LONG){
static const int bits[] = {0, 1, 8, 16, 32, 64};
ret = builder->CreateSub(getInteger(bits[type], 0), val);
} else {
ret = builder->CreateFSub(type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0), val);
}
if (on_stack)
push_value(ret, type);
return ret;
}
}
llvm::Value* CatExpression::codegen_get_value(){
codegen_on_stack();
return ret_top_stack(T_STRING, on_stack);
}
void CatExpression::codegen_on_stack(){
for(size_t i=0, e=args.size(); i!=e; i++){
args[i]->codegen_on_stack();
}
builder->CreateCall(get_global_function_jif(SUBR_cat, 'v', "h"), getInteger(16, args.size()));
}
llvm::Value* FileExpression::codegen_get_value(){
codegen_on_stack();
return ret_top_stack(T_STRING, on_stack);
}
void FileExpression::codegen_on_stack(){
for(size_t i=0, e=args.size(); i!=e; i++){
args[i]->codegen_on_stack();
}
builder->CreateCall(get_global_function_jif(SUBR_file, 'v', "h"), getInteger(16, args.size()));
}
llvm::Value* LikeExpression::codegen_get_value(){
codegen_on_stack();
return ret_top_stack(T_BOOLEAN, on_stack);
}
void LikeExpression::codegen_on_stack(){
left->codegen_on_stack();
right->codegen_on_stack();
builder->CreateCall(get_global_function_jif(SUBR_like, 'v', "h"), getInteger(16, kind));
}
llvm::Value* CheckStringExpression::codegen_get_value(){
llvm::Value* val = expr->codegen_get_value();
llvm::Value* vtype = extract_value(val, 0);
llvm::Value* vdata = extract_value(val, 1);
llvm::Value* nullstring = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0));
llvm::Value* ret = gen_if_phi(nullstring, builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_NULL)), [&](){
gen_if_noreturn(builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_STRING)), [&](){
//codegen_printf("okända typen: %ld\n", vtype);
create_throw(E_TYPE, JIF.F_TYPE_get_name(T_STRING), "(unknown)");
});
llvm::Value* strptr = builder->CreateIntToPtr(vdata, llvmType(getInt8PtrTy));
llvm::Value* len = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(strptr, llvmType(getInt32PtrTy)), getInteger(TARGET_BITS, -1)));
return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), strptr, getInteger(32, 0), len);
});
c_SP(-expr->on_stack);
if (on_stack)
push_value(ret, type);
return ret;
}
llvm::Value* CheckIntegerExpression::codegen_get_value(){
llvm::Value* ret = expr->codegen_get_value();
llvm::Value* vtype = extract_value(ret, 0);
llvm::Value* data = extract_value(ret, 1);
llvm::Value* not_ok = builder->CreateICmpUGT(vtype, getInteger(TARGET_BITS, T_INTEGER));
c_SP(-expr->on_stack);
gen_if_noreturn(not_ok, [&](){
release(ret, T_VARIANT);
create_throw(E_TYPE, JIF.F_TYPE_get_name(T_INTEGER), "(unknown)");
});
ret = builder->CreateTrunc(data, llvmType(getInt32Ty));
if (on_stack)
push_value(ret, T_INTEGER);
return ret;
}
llvm::Value* CheckFloatExpression::codegen_get_value(){
llvm::Value* ret = expr->codegen_get_value();
llvm::Value* vtype = extract_value(ret, 0);
llvm::Value* data = extract_value(ret, 1);
llvm::Value* not_ok = builder->CreateICmpUGT(vtype, getInteger(TARGET_BITS, T_FLOAT));
c_SP(-expr->on_stack);
gen_if_noreturn(not_ok, [&](){
release(ret, T_VARIANT);
create_throw(E_TYPE, JIF.F_TYPE_get_name(T_INTEGER), "(unknown)");
});
llvm::Value* low32 = builder->CreateTrunc(data, llvmType(getInt32Ty));
llvm::Value* float_from_int = builder->CreateSIToFP(low32, llvmType(getDoubleTy));
ret = gen_if_phi(float_from_int, builder->CreateICmpSGT(vtype, getInteger(TARGET_BITS, T_INTEGER)), [&](){
llvm::Value* float_data = builder->CreateBitCast(data, llvmType(getDoubleTy));
llvm::Value* single_data = builder->CreateFPExt(builder->CreateBitCast(low32, llvmType(getFloatTy)), llvmType(getDoubleTy));
llvm::Value* long_data = builder->CreateSIToFP(data, llvmType(getDoubleTy));
return builder->CreateSelect(
builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_FLOAT)),
float_data,
builder->CreateSelect(builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_SINGLE)), single_data, long_data));
});
if (on_stack)
push_value(ret, T_FLOAT);
return ret;
}
llvm::Value* CheckPointerExpression::codegen_get_value(){
llvm::Value* ret = expr->codegen_get_value();
llvm::Value* vtype = extract_value(ret, 0);
llvm::Value* data = extract_value(ret, 1);
llvm::Value* not_ok = builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_POINTER));
c_SP(-expr->on_stack);
gen_if_noreturn(not_ok, [&](){
release(ret, T_VARIANT);
create_throw(E_TYPE, JIF.F_TYPE_get_name(T_POINTER), "(unknown)");
});
ret = builder->CreateIntToPtr(data, llvmType(getInt8PtrTy));
if (on_stack)
push_value(ret, T_POINTER);
return ret;
}
static llvm::Value* gen_max(llvm::Value* val1, llvm::Value* val2){
return builder->CreateSelect(builder->CreateICmpSLT(val1, val2), val2, val1);
}
static llvm::Value* gen_min(llvm::Value* val1, llvm::Value* val2){
return builder->CreateSelect(builder->CreateICmpSLT(val1, val2), val1, val2);
}
static llvm::Value* gen_minmax(llvm::Value* val, llvm::Value* lo, llvm::Value* hi){
llvm::BasicBlock* BB1 = builder->GetInsertBlock();
llvm::BasicBlock* BB2 = create_bb("minmax1");
llvm::BasicBlock* BB3 = create_bb("minmax2");
llvm::Value* cmp = builder->CreateICmpSLT(val, lo);
builder->CreateCondBr(cmp, BB3, BB2);
builder->SetInsertPoint(BB2);
llvm::Value* cmp2 = builder->CreateICmpSGT(val, hi);
llvm::Value* r1 = builder->CreateSelect(cmp2, hi, val);
builder->CreateBr(BB3);
builder->SetInsertPoint(BB3);
llvm::PHINode* phi = builder->CreatePHI(val->getType(), 2);
phi->addIncoming(lo, BB1);
phi->addIncoming(r1, BB2);
return phi;
}
static llvm::Value* get_string(llvm::Value* str){
llvm::Value* ptr = extract_value(str, 1);
return builder->CreateGEP(ptr, to_target_int(extract_value(str, 2)));
}
static std::pair<llvm::Value*, llvm::Value*> get_string_len(llvm::Value* str){
llvm::Value* ptr = extract_value(str, 1);
ptr = builder->CreateGEP(ptr, to_target_int(extract_value(str, 2)));
return std::pair<llvm::Value*, llvm::Value*>(ptr, extract_value(str, 3));
}
static llvm::Value* codegen_spec_method(CLASS_DESC* desc, int index, bool dispatch, bool can_quick, bool object_on_stack, CLASS* klass, llvm::Value* effective_class, llvm::Value* obj, int nargs, bool noret){
llvm::Value* is_native;
llvm::Value* desc_method;
llvm::Value* desc_exec;
llvm::Value* desc_class;
if (dispatch){
int offset_table = TARGET_BITS == 64 ? 40/8 : 28/4;
int offset_desc_in_desc_symbol = TARGET_BITS == 64 ? 12 : 8;
int offset_native_flag = TARGET_BITS == 64 ? 35 : 19;
//table_addr = (char*)effective_class->table
llvm::Value* table_addr = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, pointer_t(llvmType(getInt8PtrTy))), getInteger(TARGET_BITS, offset_table)));
//desc_addr_addr = (char*)(table_addr + sizeof(CLASS_DESC_SYMBOL) * index + desc_offset)
llvm::Value* desc_addr_addr = builder->CreateGEP(table_addr, getInteger(TARGET_BITS, sizeof(CLASS_DESC_SYMBOL) * index + offset_desc_in_desc_symbol));
//desc_addr = *(char**)desc_addr_addr
llvm::Value* desc_addr = builder->CreateLoad(builder->CreateBitCast(desc_addr_addr, pointer_t(llvmType(getInt8PtrTy))));
//native_flag_addr = (char*)(desc_addr + offset_native_flag)
llvm::Value* native_flag_addr = builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offset_native_flag));
//native_subr_flag = *native_flag_addr
llvm::Value* native_subr_flag = builder->CreateLoad(native_flag_addr);
//native_flag = (bool)(native_subr_flag & 1)
llvm::Value* native_flag = builder->CreateTrunc(native_subr_flag, llvmType(getInt1Ty));
//subr_flag = (bool)(native_subr_flag & 2)
//llvm::Value* subr_flag = builder->CreateICmpNE(builder->CreateAnd(native_subr_flag, getInteger(8, 2)), getInteger(8, 0));
//llvm::Value* function_kind = builder->CreateSelect(native_flag, builder->CreateSelect(subr_flag, getInteger(8, FUNCTION_SUBR), getInteger(8, FUNCTION_NATIVE)), getInteger(8, FUNCTION_PUBLIC));
///Disable subr, because I think it will never be used this way anyway, else it is a (big) performance hit.
is_native = native_flag;
desc_method = desc_addr;
desc_exec = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offsetof(CLASS_DESC_METHOD, exec))), charPP));
desc_class = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offsetof(CLASS_DESC_METHOD, klass))), charPP));
if (!desc->method.native){
//Assume native classes don't inherit non-native ones :)
is_native = getInteger(1, false);
}
} else {
is_native = getInteger(1, desc->method.native);
desc_method = get_global((void*)&desc->method, llvmType(getInt8Ty));
desc_exec = get_global((void*)desc->method.exec, llvmType(getInt8Ty));
desc_class = get_global((void*)desc->method.klass, llvmType(getInt8Ty));
}
llvm::Value* dummy = getInteger(32, 0);
llvm::Value* ret = gen_if_else_phi(is_native, [&](){
if (can_quick){
llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"),
desc_exec,
obj,
getInteger(TARGET_BITS, desc->method.type),
builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -nargs)), llvmType(getInt8PtrTy)));
gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
llvm::Value* ret = dummy;
if (!noret){
ret = read_value(get_global(&TEMP, value_type), desc->method.type);
builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(&TEMP, LONG_TYPE));
borrow(ret, desc->method.type);
}
if (nargs > 0){
builder->CreateCall2(get_global_function_jif(RELEASE_many, 'v', "pi"),
builder->CreateBitCast(read_sp(), llvmType(getInt8PtrTy)), getInteger(32, nargs));
c_SP(-nargs);
}
unref_object(obj);
if (object_on_stack)
c_SP(-1);
if (!noret)
push_value(ret, desc->method.type);
return ret;
} else {
builder->CreateStore(desc_class, get_global((void*)&EXEC.klass));
builder->CreateStore(obj, get_global((void*)&EXEC.object));
builder->CreateStore(getInteger(32, nargs), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty)));
builder->CreateStore(desc_method, get_global((void*)&EXEC.desc));
builder->CreateStore(getInteger(8, false), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty)));
builder->CreateCall(get_global_function_jif(EXEC_native, 'v' , ""));
llvm::Value* ret = dummy;
if (!noret)
ret = read_value(get_value_on_top_addr(), desc->method.type);
unref_object(obj);
if (object_on_stack){
c_SP(-1);
if (!noret)
store_value(get_value_on_top_addr(), ret, desc->method.type);
}
if (noret)
c_SP(-1);
return ret;
}
}, [&](){
builder->CreateStore(desc_class, get_global((void*)&EXEC.klass));
builder->CreateStore(obj, get_global((void*)&EXEC.object));
builder->CreateStore(getInteger(32, nargs), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty)));
builder->CreateStore(builder->CreatePtrToInt(desc_exec, llvmType(getInt32Ty)), get_global((void*)&EXEC.index, llvmType(getInt32Ty)));
/*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"),
get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 5));*/
builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', ""));
llvm::Value* ret = dummy;
if (!noret){
ret = read_value(get_global(RP, value_type), desc->method.type);
builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(RP, LONG_TYPE));
}
unref_object(obj);
if (object_on_stack)
c_SP(-1);
if (!noret)
push_value(ret, desc->method.type);
return ret;
}, "spec_native", "spec_non_native", "spec_done");
return ret;
}
struct InlineArrayGetAddrRet {
llvm::Value* element_addr;
CLASS* klass;
CLASS* struct_class;
};
InlineArrayGetAddrRet inline_array_get_addr(std::vector<Expression*>& args, bool is_push_dynamic){
CTYPE* ctype;
CLASS* klass;
llvm::Value* array_start;
if (is_push_dynamic){
ctype = &CP->load->dyn[((PushDynamicExpression*)args[0])->index].type;
klass = CP;
array_start = builder->CreateGEP(current_op, getInteger(TARGET_BITS, ((PushDynamicExpression*)args[0])->offset));
} else {
ctype = ((ReadVariableExpression*)args[0])->ctype;
klass = ((ReadVariableExpression*)args[0])->klass;
array_start = get_global((void*)((PushStaticExpression*)args[0])->addr, llvmType(getInt8Ty));
}
CLASS_ARRAY* ca = klass->load->array[ctype->value];
int* dim = ca->dim;
int ndim = args.size()-1;
int dims[ndim];
memcpy(dims, dim, ndim*sizeof(int));
dims[ndim-1] = -dims[ndim-1];
llvm::Value* index = getInteger(32, 0);
for(int i=0; i<ndim; i++){
llvm::Value* d = args[i+1]->codegen_get_value();
gen_if_noreturn(builder->CreateICmpUGE(d, getInteger(32, dims[i])), [&](){
create_throw(E_BOUND);
});
if (i != 0)
index = builder->CreateMul(index, getInteger(32, dims[i]));
index = builder->CreateAdd(index, d);
c_SP(-args[i+1]->on_stack);
}
CLASS* struct_class = NULL;
int element_size;
if (ca->ctype.id == TC_STRUCT){
//Array of structs
struct_class = klass->load->class_ref[ca->ctype.value];
element_size = struct_class->size - sizeof(CSTRUCT);
} else {
element_size = TYPE_sizeof_memory(ctype_to_type(&ca->ctype, klass));
}
return {
builder->CreateGEP(array_start, to_target_int(builder->CreateMul(index, getInteger(32, element_size)))),
klass,
struct_class
};
}
llvm::Value* PushArrayExpression::codegen_get_value(){
int nargs = args.size();
CLASS* klass;
bool is_push_class;
bool is_push_dynamic = false;
if ( ((is_push_dynamic = isa<PushDynamicExpression>(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY)
||
(dynamic_cast<ReadVariableExpression*>(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){
auto ret = inline_array_get_addr(args, is_push_dynamic);
llvm::Value* val;
if (ret.struct_class){
//Array of structs
llvm::Value* ref_obj;
if (is_push_dynamic)
ref_obj = current_op;
else
ref_obj = get_global((void*)((ReadVariableExpression*)args[0])->klass, llvmType(getInt8Ty));
val = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"),
ref_obj,
get_global((void*)ret.struct_class, llvmType(getInt8Ty)),
ret.element_addr);
borrow_object_no_nullcheck(val);
val = get_new_struct(object_type, get_global((void*)ret.struct_class, llvmType(getInt8Ty)), val);
} else {
val = array_read(ret.element_addr, type);
}
if (on_stack)
push_value(val, type);
return val;
} else if (TYPE_is_pure_object(args[0]->type)){
klass = (CLASS*)(void*)args[0]->type;
if (klass->quick_array == CQA_ARRAY){
llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1);
llvm::Value* data;
if (args.size() == 2){
llvm::Value* index = args[1]->codegen_get_value();
//We do not check that the dimensions are correct here ... :)
int element_size = TYPE_sizeof_memory(type);
int data_offset = offsetof(CARRAY, data);
int count_offset = offsetof(CARRAY, count);
make_nullcheck(obj);
c_SP(-args[0]->on_stack-args[1]->on_stack);
llvm::Value* count = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, count_offset)), llvmType(getInt32PtrTy)));
data = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, data_offset)), charPP));
gen_if_noreturn(builder->CreateICmpUGE(index, count), [&](){
unref_object_no_nullcheck(obj);
create_throw(E_BOUND);
}, "array_get_out_of_bounds");
data = builder->CreateGEP(data, to_target_int(builder->CreateMul(index, getInteger(32, element_size))));
} else {
args[1]->codegen_on_stack();
llvm::Value* ind_addr = get_value_on_top_addr();
for(int i=2; i<nargs; i++)
args[i]->codegen_on_stack();
make_nullcheck(obj);
c_SP(-args[0]->on_stack-(nargs-1));
data = builder->CreateCall3(get_global_function_jif(CARRAY_get_data_multi, 'p', "ppi"),
obj, builder->CreateBitCast(ind_addr, llvmType(getInt8PtrTy)), getInteger(32, nargs-1));
gen_if_noreturn(builder->CreateICmpEQ(data, get_nullptr()), [&](){
unref_object_no_nullcheck(obj);
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
}
data = array_read(data, type);
unref_object_no_nullcheck(obj);
if (on_stack)
push_value(data, type);
return data;
} else if (klass->quick_array == CQA_COLLECTION){
llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1);
llvm::Value* key = args[1]->codegen_get_value();
auto str = get_string_len(key);
make_nullcheck(obj, [&](){
if (!args[1]->on_stack)
release(key, T_STRING);
});
llvm::Value* sp = builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -args[0]->on_stack-args[1]->on_stack));
llvm::Value* sp_int8 = builder->CreateBitCast(sp, llvmType(getInt8PtrTy));
/*llvm::Value* is_null =*/ builder->CreateCall4(get_global_function(GB.Collection.Get, 'c', "ppip"),
obj, str.first, str.second, sp_int8);
//type is always T_VARIANT, not T_NULL
llvm::Value* ret = builder->CreateBitCast(sp, pointer_t(value_types[T_VARIANT]));
ret = get_new_struct(variant_type, load_element(ret, 1), load_element(ret, 2));
borrow_variant(ret);
store_sp(builder->CreateGEP(sp, getInteger(TARGET_BITS, 1)));
release(key, T_STRING);
unref_object_no_nullcheck(obj);
return ret;
}
klass = (CLASS*)(void*)args[0]->type;
is_push_class = false;
goto _SPEC_GET_METHOD;
} else if (PushClassExpression* pce = dyn_cast<PushClassExpression>(args[0])){
//llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1);
klass = pce->klass;
is_push_class = true;
goto _SPEC_GET_METHOD;
} else {
for(size_t i=0, e=args.size(); i!=e; i++)
args[i]->codegen_on_stack();
builder->CreateCall(get_global_function_jif(EXEC_push_array, 'v', "h"), getInteger(16, *pc));
return ret_top_stack(T_VARIANT, on_stack);
}
_SPEC_GET_METHOD: {
int index = klass->special[SPEC_GET];
CLASS_DESC* desc = CLASS_get_desc(klass, index);
llvm::Value* obj = NULL;
llvm::Value* effective_class = NULL;
bool dispatch;
if (!is_push_class){
llvm::Value* object = args[0]->codegen_get_value();
obj = extract_value(object, 1);
if (!klass->is_virtual && !isa<PushSuperExpression>(args[0])){
make_nullcheck(obj);
effective_class = load_element(builder->CreateBitCast(obj, pointer_t(OBJECT_type)), 0);
create_check(klass, effective_class, obj);
dispatch = true;
} else {
effective_class = get_global((void*)klass, llvmType(getInt8Ty));
dispatch = false;
}
} else {
obj = get_nullptr();
effective_class = get_global((void*)klass, llvmType(getInt8Ty));
dispatch = false;
}
for(size_t i=1; i<args.size(); i++)
args[i]->codegen_on_stack();
return codegen_spec_method(desc, index, dispatch, can_quick, !is_push_class, klass, effective_class, obj, args.size()-1, false);
}
}
void PopArrayExpression::codegen(){
int nargs = args.size();
CLASS* klass;
bool is_push_class;
bool is_push_dynamic = false;
if ( ((is_push_dynamic = isa<PushDynamicExpression>(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY)
||
(dynamic_cast<ReadVariableExpression*>(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){
llvm::Value* addr = inline_array_get_addr(args, is_push_dynamic).element_addr;
llvm::Value* new_val = val->codegen_get_value();
release_variable(type, addr);
variable_write(addr, new_val, type);
c_SP(-val->on_stack); //FIXME what if destructor in release_variable throws an error?
return;
} else if (TYPE_is_pure_object(args[0]->type)){
klass = (CLASS*)(void*)args[0]->type;
if (klass->quick_array == CQA_ARRAY){
llvm::Value* new_val = val->codegen_get_value();
llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1);
llvm::Value* data;
if (args.size() == 2){
llvm::Value* index = args[1]->codegen_get_value();
//We do not check that the dimensions are correct here ... :)
int element_size = TYPE_sizeof_memory(type);
int data_offset = offsetof(CARRAY, data);
int count_offset = offsetof(CARRAY, count);
make_nullcheck(obj, [&](){
release(new_val, val->type);
});
c_SP(-val->on_stack-args[0]->on_stack-args[1]->on_stack); //FIXME what if destructor in release_variable throws an error?
llvm::Value* count = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, count_offset)), llvmType(getInt32PtrTy)));
data = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, data_offset)), charPP));
gen_if_noreturn(builder->CreateICmpUGE(index, count), [&](){
release(new_val, val->type);
unref_object_no_nullcheck(obj);
create_throw(E_BOUND);
}, "array_get_out_of_bounds");
data = builder->CreateGEP(data, to_target_int(builder->CreateMul(index, getInteger(32, element_size))));
} else {
args[1]->codegen_on_stack();
llvm::Value* ind_addr = get_value_on_top_addr();
for(int i=2; i<nargs; i++)
args[i]->codegen_on_stack();
make_nullcheck(obj, [&](){
if (!val->on_stack)
release(new_val, val->type);
});
c_SP(-val->on_stack-args[0]->on_stack-(nargs-1));
data = builder->CreateCall3(get_global_function_jif(CARRAY_get_data_multi, 'p', "ppi"),
obj, builder->CreateBitCast(ind_addr, llvmType(getInt8PtrTy)), getInteger(32, nargs-1));
gen_if_noreturn(builder->CreateICmpEQ(data, get_nullptr()), [&](){
release(new_val, val->type);
unref_object_no_nullcheck(obj);
builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', ""));
builder->CreateUnreachable();
});
}
release_variable(type, data);
variable_write(data, new_val, type);
unref_object_no_nullcheck(obj);
return;
} else if (klass->quick_array == CQA_COLLECTION){
llvm::Value* value = val->codegen_get_value();
llvm::Value* object = extract_value(args[0]->codegen_get_value(), 1);
llvm::Value* key = args[1]->codegen_get_value();
c_SP(-1-args[0]->on_stack-args[1]->on_stack);
make_nullcheck(object, [&](){
release(key, T_STRING);
});
gen_if_noreturn(builder->CreateICmpEQ(extract_value(key, 3), getInteger(32, 0)), [&](){
release(key, T_STRING);
unref_object_no_nullcheck(object);
create_throw(E_VKEY);
});
auto str = get_string_len(key);
builder->CreateCall4(get_global_function(GB.Collection.Set, 'c', "ppip"),
object, str.first, str.second, builder->CreateBitCast(read_sp(), llvmType(getInt8PtrTy)));
release(value, T_VARIANT);
release(key, T_STRING);
unref_object_no_nullcheck(object);
return;
}
klass = (CLASS*)(void*)args[0]->type;
is_push_class = false;
goto _SPEC_PUT_METHOD;
} else if (PushClassExpression* pce = dyn_cast<PushClassExpression>(args[0])){
klass = pce->klass;
is_push_class = true;
goto _SPEC_PUT_METHOD;
} else {
val->codegen_on_stack();
for(size_t i=0, e=args.size(); i!=e; i++)
args[i]->codegen_on_stack();
builder->CreateCall(get_global_function_jif(EXEC_pop_array, 'v', "h"), getInteger(16, *pc));
return;
}
_SPEC_PUT_METHOD: {
int index = klass->special[SPEC_PUT];
CLASS_DESC* desc = CLASS_get_desc(klass, index);
llvm::Value* obj = NULL;
llvm::Value* effective_class = NULL;
bool dispatch;
if (!is_push_class){
llvm::Value* object = args[0]->codegen_get_value();
obj = extract_value(object, 1);
if (!klass->is_virtual && !isa<PushSuperExpression>(args[0])){
make_nullcheck(obj);
effective_class = load_element(builder->CreateBitCast(obj, pointer_t(OBJECT_type)), 0);
create_check(klass, effective_class, obj);
dispatch = true;
} else {
effective_class = get_global((void*)klass, llvmType(getInt8Ty));
dispatch = false;
}
} else {
obj = get_nullptr();
effective_class = get_global((void*)klass, llvmType(getInt8Ty));
dispatch = false;
}
val->codegen_on_stack();
for(size_t i=1; i<args.size(); i++)
args[i]->codegen_on_stack();
codegen_spec_method(desc, index, dispatch, can_quick, !is_push_class, klass, effective_class, obj, args.size(), true);
}
}
#include "jit_codegen_conv.h"
#define codegen_stack for(size_t i=0, e=args.size(); i!=e; i++){ args[i]->must_on_stack(); args[i]->codegen_on_stack(); } stack_diff -= args.size();
#define codegen_value for(size_t i=0, e=args.size(); i!=e; i++){ param[i] = args[i]->codegen_get_value(); stack_diff -= args[i]->on_stack; }
#define SUBR_CODE(func) on_stack = true; builder->CreateCall(get_global_function_jif(func, 'v', "h"), getInteger(16, (digit << 8) | extra)); break;
#define SUBR(func) on_stack = true; builder->CreateCall(get_global_function_jif(func, 'v', "")); break;
static llvm::Value* create_phi(llvm::Value* v1, llvm::BasicBlock* BB1, llvm::Value* v2, llvm::BasicBlock* BB2){
llvm::PHINode* phi = builder->CreatePHI(v1->getType(), 2);
phi->addIncoming(v1, BB1);
phi->addIncoming(v2, BB2);
return phi;
}
llvm::Value* SubrExpression::codegen_get_value(){
int nargs = args.size();
llvm::Value* param[nargs];
int stack_diff = on_stack;
/*for(int i=0; i<nargs; i++)
stack_diff -= args[0]->on_stack;*/
switch(digit){
case 0x40: { //Left
codegen_value
llvm::Value* string_length = extract_value(param[0], 3);
llvm::Value* val = nargs == 1 ? getInteger(32, 1) : param[1];
llvm::Value* backwards = builder->CreateAdd(val, string_length);
val = builder->CreateSelect(builder->CreateICmpSLT(val, getInteger(32, 0)), backwards, val);
val = gen_minmax(val, getInteger(32, 0), string_length);
param[0] = insert_value(param[0], val, 3);
c_SP(-(args[0]->on_stack + (nargs == 2 && args[1]->on_stack)));
if (on_stack)
push_value(param[0], args[0]->type);
return param[0];
}
case 0x42: { //Right
codegen_value
llvm::Value* string_offset = extract_value(param[0], 2);
llvm::Value* string_length = extract_value(param[0], 3);
llvm::Value* val = nargs == 1 ? getInteger(32, 1) : param[1];
llvm::Value* backwards = builder->CreateAdd(val, string_length);
val = builder->CreateSelect(builder->CreateICmpSLT(val, getInteger(32, 0)), backwards, val);
llvm::Value* len = gen_minmax(val, getInteger(32, 0), string_length);
val = builder->CreateSub(string_length, len);
val = builder->CreateAdd(string_offset, val);
param[0] = insert_value(insert_value(param[0], val, 2), len, 3);
c_SP(-(args[0]->on_stack + (nargs == 2 && args[1]->on_stack)));
if (on_stack)
push_value(param[0], args[0]->type);
return param[0];
}
case 0x41: { //Mid
codegen_value
llvm::Value* start = builder->CreateSub(param[1], getInteger(32, 1));
gen_if_noreturn(builder->CreateICmpSLT(start, getInteger(32, 0)), [&](){
create_throw(E_ARG);
});
llvm::Value* string_length = extract_value(param[0], 3);
llvm::BasicBlock* BB1 = create_bb("Mid_return_empty_string");
llvm::BasicBlock* BB2 = create_bb("cont1");
llvm::BasicBlock* BB3 = create_bb("cont2");
llvm::BasicBlock* BB4 = create_bb("cont3");
builder->CreateCondBr(builder->CreateICmpSGE(start, string_length), BB1, BB2);
builder->SetInsertPoint(BB2);
llvm::Value* newlen = nargs == 2 ? string_length : param[2];
llvm::Value* string_length_minus_start = builder->CreateSub(string_length, start);
llvm::Value* newlen_if_negative = gen_max(getInteger(32, 0),
builder->CreateAdd(string_length_minus_start, newlen));
newlen = builder->CreateSelect(builder->CreateICmpSLT(newlen, getInteger(32, 0)),
newlen_if_negative, newlen);
newlen = gen_minmax(newlen, getInteger(32, 0), string_length_minus_start);
builder->CreateCondBr(builder->CreateICmpEQ(newlen, getInteger(32, 0)), BB1, BB3);
builder->SetInsertPoint(BB3);
llvm::Value* ret1 = insert_value(insert_value(param[0], builder->CreateAdd(extract_value(param[0], 2), start), 2), newlen, 3);
builder->CreateBr(BB4);
builder->SetInsertPoint(BB1);
release(param[0], args[0]->type);
llvm::BasicBlock* BB5 = builder->GetInsertBlock();
llvm::Value* ret2 = get_voidstring();
builder->CreateBr(BB4);
builder->SetInsertPoint(BB4);
param[0] = create_phi(ret1, BB3, ret2, BB5);
c_SP(-(args[0]->on_stack + (nargs == 2 && args[1]->on_stack) + (nargs == 3 && args[2]->on_stack)));
if (on_stack)
push_value(param[0], T_STRING);
return param[0];
}
case 0x43: { //Len
codegen_value
llvm::Value* string_length = extract_value(param[0], 3);
release(param[0], args[0]->type);
c_SP(-args[0]->on_stack + on_stack);
if (on_stack)
set_top_value(string_length, T_INTEGER);
return string_length;
}
case 0x44: { //Space
codegen_value
gen_if_noreturn(builder->CreateICmpSLT(param[0], getInteger(32, 0)), [&](){
create_throw(E_ARG);
});
llvm::Value* ret = gen_if_else_phi(builder->CreateICmpEQ(param[0], getInteger(32, 0)), [&](){
return get_voidstring();
}, [&](){
builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"),
builder->CreateBitCast(temp_value, llvmType(getInt8PtrTy)), get_nullptr(), param[0]);
llvm::Value* str = builder->CreateLoad(builder->CreateBitCast(temp_value, pointer_t(string_type)));
if (TARGET_BITS == 64)
param[0] = builder->CreateZExt(param[0], llvmType(getInt64Ty));
llvm::Value* ptr = extract_value(str, 1);
//llvm::Function* memset_func = llvm::Intrinsic::getDeclaration(M, llvm::Intrinsic::memset, string_to_type_vector("pj"));
const char* memset_name = TARGET_BITS == 64 ? "llvm.memset.p0i8.i64" : "llvm.memset.p0i8.i32";
llvm::Function* memset_func = llvm::cast<llvm::Function>(M->getOrInsertFunction(memset_name, get_function_type('v', "pcjib", false)));
builder->CreateCall5(memset_func, ptr, getInteger(8, ' '), param[0], getInteger(32, 0), getInteger(1, false));
borrow_string_no_nullcheck(ptr);
return str;
});
c_SP(-args[0]->on_stack + on_stack);
if (on_stack)
set_top_value(ret, T_STRING);
return ret;
}
case 0x45: //String
codegen_stack
SUBR(SUBR_string)
case 0x46: { //Trim
codegen_value
bool left = extra == 0 || extra == 1;
bool right = extra == 0 || extra == 2;
llvm::Value* string_ptr = extract_value(param[0], 1);
llvm::Value* string_offset = extract_value(param[0], 2);
llvm::Value* string_length = extract_value(param[0], 3);
string_ptr = builder->CreateGEP(string_ptr, to_target_int(string_offset));
if (left){
llvm::Value* enter_loop = gen_and_if(
builder->CreateICmpSGT(string_length, getInteger(32, 0)),
[&](){return builder->CreateICmpULE(builder->CreateLoad(string_ptr), getInteger(8, ' '));}
);
llvm::BasicBlock *from_block = builder->GetInsertBlock(), *loop_block;
llvm::Value *ret_offset, *ret_length, *ret_ptr;
gen_while(enter_loop, [&](){
llvm::PHINode* phi_offset = builder->CreatePHI(llvmType(getInt32Ty), 2);
llvm::PHINode* phi_length = builder->CreatePHI(llvmType(getInt32Ty), 2);
llvm::PHINode* phi_ptr = builder->CreatePHI(llvmType(getInt8PtrTy), 2);
ret_offset = builder->CreateAdd(phi_offset, getInteger(32, 1));
ret_length = builder->CreateSub(phi_length, getInteger(32, 1));
ret_ptr = builder->CreateGEP(phi_ptr, getInteger(TARGET_BITS, 1));
llvm::Value* ret = gen_and_if(
builder->CreateICmpSGT(ret_length, getInteger(32, 0)),
[&](){return builder->CreateICmpULE(builder->CreateLoad(ret_ptr), getInteger(8, ' ')); }
);
loop_block = builder->GetInsertBlock();
phi_offset->addIncoming(string_offset, from_block);
phi_offset->addIncoming(ret_offset, loop_block);
phi_length->addIncoming(string_length, from_block);
phi_length->addIncoming(ret_length, loop_block);
phi_ptr->addIncoming(string_ptr, from_block);
phi_ptr->addIncoming(ret_ptr, loop_block);
return ret;
});
string_ptr = create_phi(string_ptr, from_block, ret_ptr, loop_block);
string_offset = create_phi(string_offset, from_block, ret_offset, loop_block);
string_length = create_phi(string_length, from_block, ret_length, loop_block);
}
if (right){
llvm::Value* enter_loop = gen_and_if(
builder->CreateICmpSGT(string_length, getInteger(32, 0)),
[&](){return builder->CreateICmpULE(
builder->CreateLoad(
builder->CreateGEP(
string_ptr,
to_target_int(builder->CreateSub(string_length, getInteger(32, 1)))
)
), getInteger(8, ' ')
);}
);
llvm::BasicBlock *from_block = builder->GetInsertBlock(), *loop_block;
llvm::Value* ret_length;
gen_while(enter_loop, [&](){
llvm::PHINode* phi_length = builder->CreatePHI(llvmType(getInt32Ty), 2);
ret_length = builder->CreateSub(phi_length, getInteger(32, 1));
llvm::Value* ret = gen_and_if(
builder->CreateICmpSGT(ret_length, getInteger(32, 0)),
[&](){return builder->CreateICmpULE(
builder->CreateLoad(
builder->CreateGEP(
string_ptr,
to_target_int(builder->CreateSub(ret_length, getInteger(32, 1)))
)
), getInteger(8, ' ')
);}
);
loop_block = builder->GetInsertBlock();
phi_length->addIncoming(string_length, from_block);
phi_length->addIncoming(ret_length, loop_block);
return ret;
});
string_length = create_phi(string_length, from_block, ret_length, loop_block);
}
llvm::Value* ret = insert_value(insert_value(param[0], string_offset, 2), string_length, 3);
c_SP(-args[0]->on_stack + on_stack);
if (on_stack)
set_top_value(ret, args[0]->type);
return ret;
}
case 0x47:
case 0x48: { //UCase, LCase
codegen_stack
SUBR_CODE(SUBR_upper)
}
case 0x49: { //Chr
codegen_value
gen_if_noreturn(builder->CreateICmpUGT(param[0], getInteger(32, 255)), [&](){
create_throw(E_ARG);
}, "Chr_out_of_range", "Chr_ok");
llvm::Value* arr = get_global((void*)STRING_char_string, llvmType(getInt8Ty));
param[0] = builder->CreateMul(param[0], getInteger(32, 2));
if (TARGET_BITS == 64)
param[0] = builder->CreateZExt(param[0], llvmType(getInt64Ty));
llvm::Value* retaddr = builder->CreateGEP(arr, param[0]);
llvm::Value* ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), retaddr, getInteger(32, 0), getInteger(32, 1));
c_SP(-args[0]->on_stack + on_stack);
if (on_stack)
set_top_value(ret, T_CSTRING);
return ret;
}
case 0x4A: { //Asc
codegen_value
llvm::Value* pos = nargs == 2 ? builder->CreateSub(param[1], getInteger(32, 1)) : getInteger(32, 0);
llvm::Value* strptr = extract_value(param[0], 1);
llvm::Value* ret = gen_if_else_phi(builder->CreateOr(builder->CreateICmpSLT(pos, getInteger(32, 0)), builder->CreateICmpSGE(pos, extract_value(param[0], 3))), [&](){
return getInteger(32, 0);
}, [&](){
pos = builder->CreateAdd(pos, extract_value(param[0], 2));
llvm::Value* target_type_pos = TARGET_BITS == 64 ? builder->CreateZExt(pos, llvmType(getInt64Ty)) : pos;
llvm::Value* addr = builder->CreateGEP(strptr, target_type_pos);
return builder->CreateZExt(builder->CreateLoad(addr), llvmType(getInt32Ty));
}, "empty_string", "not_empty_string", "Asc_done");
release(param[0], args[0]->type);
c_SP(stack_diff);
if (on_stack)
set_top_value(ret, T_INTEGER);
return ret;
}
case 0x4B: //InStr
case 0x4C: //RInStr
codegen_stack
SUBR_CODE(SUBR_instr)
case 0x4D: //Subst
codegen_stack
SUBR_CODE(SUBR_subst)
case 0x4E: //Replace
codegen_stack
SUBR_CODE(SUBR_replace)
case 0x4F: //Split
codegen_stack
SUBR_CODE(SUBR_split)
case 0x50: { //Scan
codegen_value
auto str = get_string_len(param[0]);
auto pat = get_string_len(param[1]);
llvm::Value* arr = builder->CreateCall4(get_global_function_jif(OBJECT_create, 'p', "pppi"), get_global((void*)GB.FindClass("String[]"), llvmType(getInt8Ty)), get_nullptr(), get_nullptr(), getInteger(32, 0));
gen_if(builder->CreateAnd(
builder->CreateICmpNE(str.second, getInteger(32, 0)),
builder->CreateICmpNE(pat.second, getInteger(32, 0))), [&](){
builder->CreateCall5(get_global_function_jif(REGEXP_scan, 'c', "ppipi"), arr, pat.first, pat.second, str.first, str.second);
}, "scan_ok_params", "scan_empty_params");
llvm::Value* ret = get_new_struct(object_type, get_global((void*)GB.FindClass("String[]"), llvmType(getInt8Ty)), arr);
borrow_object_no_nullcheck(arr);
release(param[0], args[0]->type);
release(param[1], args[1]->type);
c_SP(-args[0]->on_stack-args[1]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, (TYPE)(void*)GB.FindClass("String[]"));
return ret;
}
case 0x51: //Comp
codegen_stack
SUBR_CODE(SUBR_strcomp)
case 0x52: { //Conv
codegen_value
auto str = get_string_len(param[0]);
llvm::Value* src = get_string(param[1]);
llvm::Value* dst = get_string(param[2]);
llvm::Value* llvm_args[6] = {builder->CreateBitCast(temp_voidptr, llvmType(getInt8PtrTy)), str.first, str.second, src, dst, getInteger(8, false)};
llvm::Value* conv_error = builder->CreateCall(get_global_function_jif(STRING_conv, 'i', "ppippc"), llvm_args);
release(param[0], args[0]->type);
release(param[1], args[1]->type);
release(param[2], args[2]->type);
c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack+on_stack);
gen_if_noreturn(builder->CreateICmpNE(conv_error, getInteger(32, 0)), [&](){
create_throw(conv_error);
}, "iconv_error", "iconv_ok");
llvm::Value* result = builder->CreateLoad(temp_voidptr);
llvm::Value* ret = gen_if_else_phi(builder->CreateICmpEQ(result, get_nullptr()), [&](){
return get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0));
}, [&](){
borrow_string_no_nullcheck(result); //FIXME no nullcheck ok?
llvm::Value* len = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(result, getInteger(TARGET_BITS, -4)), llvmType(getInt32PtrTy)));
return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), result, getInteger(32, 0), len);
});
if (on_stack)
set_top_value(ret, T_STRING);
return ret;
}
case 0x53: { //DConv, SConv
/*auto str = get_string_len(param[0]);
llvm::Value* src = builder->CreateIntToPtr(getInteger(TARGET_BITS, -2), llvmType(getInt8PtrTy));
llvm::Value* dst = get_global(;*/
codegen_stack
SUBR_CODE(SUBR_sconv)
}
case 0x54: //Abs
if (type != T_VARIANT){
codegen_value
llvm::Value* ret;
switch(type){
#define ab(bits) ret = builder->CreateSelect(builder->CreateICmpSLT(param[0], getInteger(bits, 0)), builder->CreateSub(getInteger(bits, 0), param[0]), param[0]); break;
#define fab(zero) ret = builder->CreateSelect(builder->CreateFCmpULT(param[0], getFloat(zero)), builder->CreateFSub(getFloat(zero), param[0]), param[0]); break;
case T_BOOLEAN:
case T_BYTE: ret = param[0]; break;
case T_SHORT: ab(16)
case T_INTEGER: ab(32)
case T_LONG: ab(64)
case T_SINGLE: fab(0.0f)
case T_FLOAT: fab(0.0)
#undef ab
#undef fab
default: abort();
}
c_SP(-args[0]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type, args[0]->on_stack ? false : true);
return ret;
} else {
//store_pc(pc); Should not be needed...
codegen_stack
SUBR_CODE(SUBR_abs)
}
case 0x55: //Int
if (type != T_VARIANT){
codegen_value
llvm::Value* ret = param[0];
if (type == T_SINGLE)
ret = builder->CreateCall(get_global_function(floorf, 'f', "f"), param[0]);
else if (type == T_FLOAT)
ret = builder->CreateCall(get_global_function(floor, 'd', "d"), param[0]);
c_SP(-args[0]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type, args[0]->on_stack ? false : true);
return ret;
} else {
codegen_stack
SUBR_CODE(SUBR_int)
}
case 0x56: //Fix
if (type != T_VARIANT){
codegen_value
llvm::Value* ret = param[0];
if (type >= T_SINGLE){
llvm::Value* zero = type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0);
llvm::Value* func1 = type == T_SINGLE ? get_global_function(floorf, 'f', "f") : get_global_function(floor, 'd', "d");
llvm::Value* func2 = type == T_SINGLE ? get_global_function(fabsf, 'f', "f") : get_global_function(fabs, 'd', "d");
ret = gen_if_else_phi(builder->CreateFCmpUGE(param[0], zero), [&](){
return builder->CreateCall(func1, param[0]);
}, [&](){
return builder->CreateFSub(zero, builder->CreateCall(func1, builder->CreateCall(func2, param[0])));
});
}
c_SP(-args[0]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type, args[0]->on_stack ? false : true);
return ret;
} else {
codegen_stack
SUBR_CODE(SUBR_fix)
}
case 0x57: //Sgn
if (args[0]->type != T_VARIANT){
llvm::Type* intTy = llvmType(getInt32Ty);
static const int bits[] = {0, 1, 8, 16, 32, 64};
static llvm::Value* const zero[] = {NULL, getInteger(1, 0), getInteger(8, 0), getInteger(16, 0), getInteger(32, 0), getInteger(64, 0), getFloat(0.0f), getFloat(0.0)};
codegen_value
llvm::Value* ret;
TYPE t = args[0]->type;
switch(t){
case T_BOOLEAN: ret = builder->CreateSExt(param[0], intTy); break;
case T_BYTE: ret = builder->CreateZExt(builder->CreateICmpNE(param[0], zero[t]), intTy); break;
case T_SHORT:
case T_INTEGER:
case T_LONG: {
llvm::Value* lobit = builder->CreateAShr(param[0], bits[t]-1);
if (t < T_INTEGER) lobit = builder->CreateSExt(param[0], intTy);
else if (t > T_INTEGER) lobit = builder->CreateTrunc(param[0], intTy);
ret = builder->CreateSelect(builder->CreateICmpSGT(param[0], zero[t]), getInteger(32, 1), lobit);
break;
}
case T_SINGLE:
case T_FLOAT: {
ret = builder->CreateSelect(builder->CreateFCmpOGT(param[0], zero[t]), getInteger(32, 1),
builder->CreateSelect(builder->CreateFCmpOLT(param[0], zero[t]), getInteger(32, -1), zero[T_INTEGER]));
break;
}
default: __builtin_unreachable();
}
c_SP(-args[0]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type, args[0]->on_stack ? false : true);
return ret;
} else {
codegen_stack
SUBR_CODE(SUBR_sgn)
}
case 0x58: { //Math
codegen_value
llvm::Value* ret;
if (extra == 1){
//frac
llvm::Value* x = builder->CreateCall(get_global_function(fabs, 'd', "d"), param[0]);
ret = builder->CreateFSub(x, builder->CreateCall(get_global_function(floor, 'd', "d"), x));
} else if (extra == 11){
//deg
ret = builder->CreateFDiv(builder->CreateFMul(param[0], getFloat(180.0)), getFloat(M_PI));
} else if (extra == 12){
//rad
ret = builder->CreateFDiv(builder->CreateFMul(param[0], getFloat(M_PI)), getFloat(180.0));
} else {
#define f(name) get_global_function(name, 'd', "d")
llvm::Value* functions[28] = {
NULL, NULL, f(log), f(exp), f(sqrt), f(sin), f(cos), f(tan), f(atan),
f(asin), f(acos), NULL, NULL, f(log10), f(sinh), f(cosh), f(tanh), f(asinh),
f(acosh), f(atanh), f(exp2), f(exp10), f(log2), f(cbrt), f(expm1), f(log1p),
f(floor), f(ceil)
};
#undef f
ret = builder->CreateCall(functions[extra], param[0]);
}
gen_if_noreturn(builder->CreateICmpEQ(builder->CreateCall(get_global_function(__finite, 'i', "d"), ret), getInteger(32, 0)), [&](){
create_throw(E_MATH);
});
c_SP(-args[0]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type, args[0]->on_stack ? false : true);
return ret;
}
case 0x59: { //Pi
if (nargs == 0){
if (on_stack)
push_value(getFloat(M_PI), T_FLOAT);
return getFloat(M_PI);
} else {
codegen_value
llvm::Value* ret = builder->CreateFMul(getFloat(M_PI), param[0]);
c_SP(-args[0]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, T_FLOAT, args[0]->on_stack ? false : true);
return ret;
}
}
case 0x5A: { //Round
codegen_value
llvm::Value* val = nargs == 2 ? param[1] : getInteger(32, 0);
llvm::Value* power = builder->CreateCall2(M->getOrInsertFunction("llvm.powi.f64", get_function_type('d', "di")), getFloat(10.0), val);
llvm::Value* ret = builder->CreateFMul(
builder->CreateCall(get_global_function(floor, 'd', "d"),
builder->CreateFAdd(
builder->CreateFDiv(param[0], power),
getFloat(0.5)
)
), power
);
c_SP(-args[0]->on_stack-(nargs == 2 && args[1]->on_stack)+on_stack);
if (on_stack)
set_top_value(ret, T_FLOAT, args[0]->on_stack ? false : true);
return ret;
}
case 0x5B: { //Randomize
codegen_value
llvm::Value* set = getInteger(8, nargs == 0 ? false : true);
llvm::Value* seed = nargs == 0 ? getInteger(32, 0) : param[0];
builder->CreateCall2(get_global_function_jif(randomize, 'v', "ci"), set, seed);
c_SP(-(nargs && args[0]->on_stack)+on_stack);
if (on_stack)
set_top_value(NULL, T_VOID);
return NULL;
}
case 0x5C: { //Rnd
codegen_value
llvm::Value *min, *max;
int stackdiff = on_stack;
if (nargs == 0){
min = getFloat(0.0);
max = getFloat(1.0);
} else if (nargs == 1){
min = getFloat(0.0);
max = param[0];
stackdiff -= args[0]->on_stack;
} else if (nargs == 2){
min = param[0];
max = param[1];
stackdiff -= args[0]->on_stack;
stackdiff -= args[1]->on_stack;
}
llvm::Value* r = builder->CreateCall(get_global_function_jif(rnd, 'd', ""));
llvm::Value* ret = builder->CreateFAdd(builder->CreateFMul(r, builder->CreateFSub(max, min)), min);
c_SP(stackdiff);
if (on_stack)
set_top_value(ret, T_FLOAT, (nargs >= 1 && args[0]->on_stack) ? false : true);
return ret;
}
case 0x5D: //Min
case 0x5E: { //Max
if (type != T_VARIANT){
bool is_max = digit == 0x5E;
codegen_value
llvm::Value* ret;
if (type == T_BOOLEAN || (type >= T_SHORT && type <= T_LONG))
ret = (is_max ? gen_max : gen_min)(param[0], param[1]);
else if (type == T_BYTE)
ret = builder->CreateSelect(builder->CreateICmpULT(param[0], param[1]), is_max ? param[1] : param[0], is_max ? param[0] : param[1]);
else if (type == T_FLOAT)
ret = builder->CreateSelect(builder->CreateFCmpOLT(param[0], param[1]), is_max ? param[1] : param[0], is_max ? param[0] : param[1]);
else if (type == T_DATE)
ret = builder->CreateSelect(LessDate(param[0], param[1]), is_max ? param[0] : param[1], is_max ? param[1] : param[0]);
c_SP(-args[0]->on_stack-args[1]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type, args[0]->on_stack ? false : true);
return ret;
} else {
codegen_stack
extra = digit << 8;
SUBR_CODE(SUBR_min_max)
}
}
case 0x5F: { //IIf
//FIXME if a destructor throws an error, this does not work ...
codegen_value
llvm::Value* ret;
if (args[1]->type == args[2]->type || (TYPE_is_string(args[1]->type) && TYPE_is_string(args[2]->type))){
ret = builder->CreateSelect(param[0], param[1], param[2]);
gen_if_else(param[0], [&](){
release(param[2], args[2]->type);
}, [&](){
release(param[1], args[1]->type);
}, "IIf_release_false_argument", "IIf_release_true_argument", "IIf_release_done");
c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type);
return ret;
} else {
//Convert to variant
c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack);
args[1]->on_stack = false;
args[2]->on_stack = false;
ret = gen_if_else_phi(param[0], [&](){
if (args[1]->type == type)
return param[1];
release(param[2], args[2]->type);
return JIT_conv_to_variant(args[1], param[1], on_stack, NULL);
}, [&](){
if (args[2]->type == type)
return param[2];
release(param[1], args[1]->type);
return JIT_conv_to_variant(args[2], param[2], on_stack, NULL);
}, "IIf_then", "IIf_else");
return ret;
}
break;
}
case 0x60: { //Choose
llvm::Value* ret;
if (nargs < 4){
codegen_value
ret = get_default(type);
}
if (nargs == 1){
c_SP(-args[0]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type);
return ret;
} else if (nargs == 2){
llvm::Value* cmp = builder->CreateICmpEQ(param[0], getInteger(32, 1));
ret = builder->CreateSelect(cmp, param[1], ret);
c_SP(-args[0]->on_stack-args[1]->on_stack+on_stack);
if (on_stack)
set_top_value(ret, type);
gen_if(builder->CreateXor(cmp, getInteger(1, true)), [&](){
release(param[1], args[1]->type);
}, "release_1st_choose");
return ret;
} else if (nargs == 3){
llvm::Value* index = builder->CreateSub(param[0], getInteger(32, 1));
c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack);
args[1]->on_stack = false;
args[2]->on_stack = false;
bool stack_already_set_in_conv = false;
ret = gen_if_else_phi(builder->CreateICmpULT(index, getInteger(32, 2)), [&](){
llvm::Value* r;
if (args[1]->type == args[2]->type || (TYPE_is_string(args[1]->type) && TYPE_is_string(args[2]->type))){
r = builder->CreateSelect(builder->CreateTrunc(index, llvmType(getInt1Ty)), param[2], param[1]);
c_SP(on_stack);
return r;
} else {
r = gen_if_else_phi(builder->CreateTrunc(index, llvmType(getInt1Ty)), [&](){
return JIT_conv_to_variant(args[2], param[2], on_stack, NULL);
}, [&](){
return JIT_conv_to_variant(args[1], param[1], on_stack, NULL);
});
stack_already_set_in_conv = true;
return r;
}
}, [&](){
c_SP(on_stack);
if (stack_already_set_in_conv && on_stack)
set_top_value(ret, type);
return ret;
});
if (!stack_already_set_in_conv && on_stack)
set_top_value(ret, type);
gen_if(builder->CreateICmpNE(param[0], getInteger(32, 1)), [&](){
release(param[1], args[1]->type);
}, "release_1st_choose");
gen_if(builder->CreateICmpNE(param[0], getInteger(32, 2)), [&](){
release(param[2], args[2]->type);
}, "release_2st_choose");
return ret;
} else {
codegen_stack
SUBR_CODE(SUBR_choose)
}
}
case 0x61: { //Array
codegen_value
if (nargs > 0){
push_value(getInteger(32, nargs), T_INTEGER);
}
llvm::Value* arr = builder->CreateCall4(get_global_function_jif(OBJECT_create, 'p', "pppi"), get_global((void*)type, llvmType(getInt8Ty)), get_nullptr(), get_nullptr(), getInteger(32, nargs > 0 ? 1 : 0));
borrow_object_no_nullcheck(arr);
int element_size = TYPE_sizeof_memory(type2);
int data_offset = offsetof(CARRAY, data);
llvm::Value* data = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(arr, getInteger(TARGET_BITS, data_offset)), charPP));
for(int i=0; i<nargs; i++){
llvm::Value* addr = builder->CreateGEP(data, getInteger(TARGET_BITS, i*element_size));
variable_write(addr, param[i], args[i]->type);
}
llvm::Value* ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), arr);
c_SP(stack_diff);
if (on_stack)
set_top_value(ret, type);
return ret;
}
case 0x62: { //Math2
codegen_value
llvm::Value* ret;
if (extra == 2){
llvm::Value* temp = param[0];
param[0] = param[1];
param[1] = temp;
}
if (extra <= 2){
//Atan2, Ang
ret = builder->CreateCall2(get_global_function(atan2, 'd', "dd"), param[0], param[1]);
} else {
ret = builder->CreateCall(get_global_function(sqrt, 'd', "d"), builder->CreateFAdd(builder->CreateFMul(param[0], param[0]), builder->CreateFMul(param[1], param[1])));
}
gen_if_noreturn(builder->CreateICmpEQ(builder->CreateCall(get_global_function(__finite, 'i', "d"), ret), getInteger(32, 0)), [&](){
create_throw(E_MATH);
});
c_SP(stack_diff);
if (on_stack)
set_top_value(ret, T_FLOAT);
return ret;
}
case 0x63: { //IsAscii, IsLetter, ...
codegen_value
llvm::Value* orig_ptr = extract_value(param[0], 1);
llvm::Value* ptr = builder->CreateGEP(orig_ptr, extract_value(param[0], 2));
llvm::Value* endptr = builder->CreateGEP(ptr, to_target_int(extract_value(param[0], 3)));
llvm::BasicBlock* from_block = builder->GetInsertBlock();
llvm::BasicBlock* body_block = create_bb("IsChr...");
llvm::BasicBlock* cont_block = create_bb("IsChr_done");
builder->CreateCondBr(builder->CreateICmpNE(extract_value(param[0], 3), getInteger(32, 0)), body_block, cont_block);
builder->SetInsertPoint(body_block);
llvm::PHINode* current_ptr = builder->CreatePHI(llvmType(getInt8PtrTy), 2);
llvm::Value* next = builder->CreateGEP(current_ptr, getInteger(TARGET_BITS, 1));
current_ptr->addIncoming(ptr, from_block);
current_ptr->addIncoming(next, body_block);
llvm::Value* c = builder->CreateLoad(current_ptr);
llvm::Value* res;
switch(extra){
case 1: //IsAscii:
res = builder->CreateICmpEQ(builder->CreateAnd(c, getInteger(8, ~0x7F)), getInteger(8, 0));
break;
case 2: //IsLetter:
res = builder->CreateOr(
builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, 'a')),
builder->CreateICmpSLE(c, getInteger(8, 'z'))
), builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, 'A')),
builder->CreateICmpSLE(c, getInteger(8, 'Z'))
)
); break;
case 3: //IsLower:
res = builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, 'a')),
builder->CreateICmpSLE(c, getInteger(8, 'z'))
); break;
case 4: //IsUpper:
res = builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, 'A')),
builder->CreateICmpSLE(c, getInteger(8, 'Z'))
); break;
case 5: //IsDigit:
res = builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, '0')),
builder->CreateICmpSLE(c, getInteger(8, '9'))
); break;
case 6: //IsHexa:
res = builder->CreateOr(
builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, '0')),
builder->CreateICmpSLE(c, getInteger(8, '9'))
),
builder->CreateOr(
builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, 'a')),
builder->CreateICmpSLE(c, getInteger(8, 'f'))
), builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, 'A')),
builder->CreateICmpSLE(c, getInteger(8, 'F'))
)
)
); break;
case 7: { //IsSpace:
const char* conds = " \n\r\t\f\v";
res = builder->CreateICmpEQ(c, getInteger(8, conds[0]));
for(size_t i=1; i<strlen(conds); i++)
res = builder->CreateOr(res, builder->CreateICmpEQ(c, getInteger(8, conds[i])));
break;
}
case 8: //IsBlank:
res = builder->CreateOr(builder->CreateICmpEQ(c, getInteger(8, 32)), builder->CreateICmpEQ(c, getInteger(8, '\t')));
break;
case 9: //IsPunct:
res = builder->CreateAnd(
builder->CreateICmpSGT(c, getInteger(8, 32)),
builder->CreateXor(
builder->CreateOr(
builder->CreateOr(
builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, 'a')),
builder->CreateICmpSLE(c, getInteger(8, 'z'))
), builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, 'A')),
builder->CreateICmpSLE(c, getInteger(8, 'Z'))
)
),
builder->CreateAnd(
builder->CreateICmpSGE(c, getInteger(8, '0')),
builder->CreateICmpSLE(c, getInteger(8, '9'))
)
),
getInteger(1, 1)
)
);
break;
}
llvm::Value* still_inside_string = builder->CreateICmpNE(next, endptr);
builder->CreateCondBr(builder->CreateAnd(still_inside_string, res), body_block, cont_block);
builder->SetInsertPoint(cont_block);
res = create_phi(getInteger(1, false), from_block, res, body_block);
release(param[0], args[0]->type);
c_SP(stack_diff);
if (on_stack)
set_top_value(res, T_BOOLEAN);
return res;
}
case 0x64: //Bit manipulation
if (args[0]->type == T_VARIANT){
codegen_stack
SUBR_CODE(SUBR_bit)
} else {
codegen_value
static const int bits[] = {0, 0, 8, 16, 32, 64};
int nbits = bits[args[0]->type];
gen_if_noreturn(builder->CreateICmpUGE(param[1], getInteger(32, nbits)), [&](){
create_throw(E_ARG);
}, "bit_out_of_range");
llvm::Value* bt = nbits == 32 ? param[1] : (nbits == 64 ? builder->CreateZExt(param[1], llvmType(getInt64Ty)) : builder->CreateTrunc(param[1], param[0]->getType()));
llvm::Value* res;
switch(extra){
case 1: //BClr
res = builder->CreateAnd(param[0], builder->CreateXor(builder->CreateShl(getInteger(nbits, 1), bt), getInteger(nbits, -1)));
break;
case 2: //BSet
res = builder->CreateOr(param[0], builder->CreateShl(getInteger(nbits, 1), bt));
break;
case 3: //BTst
res = builder->CreateICmpNE(builder->CreateAnd(param[0], builder->CreateShl(getInteger(nbits, 1), bt)), getInteger(nbits, 0));
break;
case 4: //BChg:
res = builder->CreateXor(param[0], builder->CreateShl(getInteger(nbits, 1), bt));
break;
case 5: //Asl:
res = builder->CreateShl(param[0], bt);
if (nbits != 8){
llvm::Value* max_signed_int = getInteger(nbits, (1ULL << nbits)-1); //0x7F...
llvm::Value* min_signed_int = getInteger(nbits, 1ULL << nbits); //0x80...
res = builder->CreateOr(
builder->CreateAnd(res, max_signed_int), builder->CreateAnd(param[0], min_signed_int)
);
}
break;
case 6: //Asr:
res = builder->CreateAShr(param[0], bt);
break;
case 7: //Rol:
res = builder->CreateOr(
builder->CreateShl(param[0], bt), builder->CreateLShr(param[0], builder->CreateSub(getInteger(nbits, nbits), bt))
);
break;
case 8: //Ror:
res = builder->CreateOr(
builder->CreateLShr(param[0], bt), builder->CreateShl(param[0], builder->CreateSub(getInteger(nbits, nbits), bt))
);
break;
case 9: //Lsl:
res = builder->CreateShl(param[0], bt);
break;
case 10: //Lsr:
res = builder->CreateLShr(param[0], bt);
break;
}
c_SP(stack_diff);
if (on_stack)
set_top_value(res, type, extra == 3 || !args[0]->on_stack);
return res;
}
case 0x65: //IsBoolean ...
codegen_stack
SUBR_CODE(SUBR_is_type)
case 0x66: { //TypeOf
codegen_value
llvm::Value* ret;
if (extra){ //Sizeof
ret = gen_if_phi(getInteger(32, TARGET_BITS/8), builder->CreateICmpULT(param[0], getInteger(32, 16)), [&](){
llvm::Value* s = builder->CreateLoad(builder->CreateGEP(get_global((void*)TYPE_sizeof_memory_tab, LONG_TYPE), to_target_int(param[0])));
if (TARGET_BITS == 64)
s = builder->CreateTrunc(s, llvmType(getInt32Ty));
return s;
}, "SizeOf");
c_SP(stack_diff);
if (on_stack)
set_top_value(ret, T_INTEGER, !args[0]->on_stack);
} else { //TypeOf
TYPE t = args[0]->type;
if (t == T_VARIANT){
ret = extract_value(param[0], 0);
if (TARGET_BITS == 64)
ret = builder->CreateTrunc(ret, llvmType(getInt32Ty));
llvm::BasicBlock *BB0 = builder->GetInsertBlock(), *BB1 = create_bb("TypeOf1"), *BB2 = create_bb("TypeOf2"), *BB3 = create_bb("TypeOf3");
builder->CreateCondBr(builder->CreateICmpEQ(ret, getInteger(32, T_CSTRING)), BB3, BB1);
builder->SetInsertPoint(BB1);
builder->CreateCondBr(builder->CreateICmpSGE(ret, getInteger(32, T_OBJECT)), BB2, BB3);
builder->SetInsertPoint(BB2);
builder->CreateBr(BB3);
builder->SetInsertPoint(BB3);
llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt32Ty), 3);
phi->addIncoming(getInteger(32, T_OBJECT), BB2);
phi->addIncoming(ret, BB1);
phi->addIncoming(getInteger(32, T_STRING), BB0);
ret = phi;
} else if (t == T_CSTRING){
ret = getInteger(32, T_STRING);
} else if (TYPE_is_object(t) && t != T_NULL){
ret = getInteger(32, T_OBJECT);
} else {
ret = getInteger(32, t);
}
c_SP(stack_diff);
if (on_stack)
set_top_value(ret, T_INTEGER, args[0]->type != T_INTEGER || !args[0]->on_stack);
}
release(param[0], args[0]->type);
return ret;
}
case 0x68: //Bin
case 0x69: //Hex
codegen_stack
SUBR_CODE(SUBR_hex_bin)
case 0x6A: //Val
codegen_stack
SUBR_CODE(SUBR_val)
case 0x6B: { //Str
args[0]->must_on_stack();
codegen_value
llvm::Value* stack_top = get_value_on_top_addr();
builder->CreateCall3(get_global_function_jif(VALUE_to_string, 'v', "ppp"),
builder->CreateBitCast(stack_top, llvmType(getInt8PtrTy)),
builder->CreateBitCast(temp_voidptr, llvmType(getInt8PtrTy)),
builder->CreateBitCast(temp_int, llvmType(getInt8PtrTy))
);
builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"),
builder->CreateBitCast(on_stack ? stack_top : temp_value, llvmType(getInt8PtrTy)),
builder->CreateLoad(temp_voidptr),
builder->CreateLoad(temp_int)
);
llvm::Value* str = read_value(on_stack ? stack_top : temp_value, T_STRING);
release(param[0], args[0]->type);
borrow_string(extract_value(str, 1)); //Nullcheck needed, might be empty string = null pointer
if (!on_stack)
c_SP(-1);
return str;
}
case 0x6C: //Format
codegen_stack
SUBR_CODE(SUBR_format)
case 0x6D: { //Timer
builder->CreateCall2(get_global_function_jif(DATE_timer, 'v', "pi"),
builder->CreateBitCast(temp_double, llvmType(getInt8PtrTy)),
getInteger(32, 1));
llvm::Value* ret = builder->CreateLoad(temp_double);
if (on_stack)
push_value(ret, T_FLOAT);
return ret;
}
case 0x6E: { //Now
c_SP(on_stack);
llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value;
builder->CreateCall(get_global_function_jif(DATE_now, 'v', "p"),
builder->CreateBitCast(addr, llvmType(getInt8PtrTy)));
return read_value(addr, T_DATE);
}
case 0x6F: //Year
codegen_stack
SUBR_CODE(SUBR_year)
case 0x70: //Week
codegen_stack
SUBR_CODE(SUBR_week)
case 0x71: //Date
codegen_stack
SUBR_CODE(SUBR_date)
case 0x72: //Time
codegen_stack
SUBR_CODE(SUBR_time)
case 0x73: //DateAdd, DateDiff
if (extra == 0){ //DateAdd
if (on_stack)
args[0]->must_on_stack();
codegen_value
c_SP(stack_diff);
llvm::Value* addr = args[0]->on_stack ? get_value_on_top_addr() : temp_value;
if (!args[0]>on_stack)
store_value(temp_value, param[0], T_DATE, false /*not needed*/);
builder->CreateCall3(get_global_function_jif(DATE_add, 'v', "pii"),
builder->CreateBitCast(addr, llvmType(getInt8PtrTy)),
param[1], param[2]);
return read_value(addr, T_DATE);
} else { //DateDiff
codegen_value
c_SP(stack_diff);
llvm::Value* addr1 = args[0]->on_stack ? get_value_on_top_addr() : temp_value2;
llvm::Value* addr2 = args[1]->on_stack ? (args[0]->on_stack ? builder->CreateGEP(addr1, getInteger(TARGET_BITS, 1)) : get_value_on_top_addr()) : temp_value;
if (!args[0]->on_stack)
store_value(temp_value2, param[0], T_DATE, false);
if (!args[1]->on_stack)
store_value(temp_value, param[1], T_DATE, false);
llvm::Value* ret = builder->CreateCall3(get_global_function_jif(DATE_diff, 'i', "ppi"),
builder->CreateBitCast(addr2, llvmType(getInt8PtrTy)),
builder->CreateBitCast(addr1, llvmType(getInt8PtrTy)),
param[2]);
if (on_stack)
set_top_value(ret, T_INTEGER);
return ret;
}
case 0x74: //Eval
codegen_stack
SUBR_CODE(SUBR_eval)
case 0x75: { //Error
//llvm::Value* ret = builder->CreateICmpNE(read_global((void*)&EXEC_got_error, llvmType(getInt8Ty)), getInteger(8, 0));
llvm::Value* ret = builder->CreateLoad(temp_got_error);
if (on_stack)
push_value(ret, T_BOOLEAN);
return ret;
}
case 0x76: //Debug
store_pc(pc);
SUBR(SUBR_debug)
case 0x77: //Wait
codegen_stack
SUBR_CODE(SUBR_wait)
case 0x78: //Open
codegen_stack
SUBR_CODE(SUBR_open)
case 0x79: //Close
codegen_stack
SUBR_CODE(SUBR_close)
case 0x7A: //Input
codegen_stack
SUBR_CODE(SUBR_input)
case 0x7B: //Line Input
codegen_stack
SUBR(SUBR_linput)
case 0x7C: //Print
codegen_stack
SUBR_CODE(SUBR_print)
case 0x7D: //Read
codegen_stack
SUBR_CODE(SUBR_read)
case 0x7E: //Write
codegen_stack
SUBR_CODE(SUBR_write)
case 0x7F: //Flush
codegen_stack
SUBR(SUBR_flush)
case 0x80: //Lock
codegen_stack
SUBR(SUBR_lock)
case 0x81: //Input From, Output To, Error To
codegen_stack
SUBR_CODE(SUBR_inp_out)
case 0x82: //Eof
codegen_stack
SUBR_CODE(SUBR_eof)
case 0x83: //Lof
codegen_stack
SUBR_CODE(SUBR_lof)
case 0x84: //Seek
codegen_stack
SUBR_CODE(SUBR_seek)
case 0x86: //Mkdir, deprecated -> Even() & Odd()
if (extra != 0){
codegen_value
llvm::Value* val = builder->CreateTrunc(param[0], llvmType(getInt1Ty));
if (extra == 1) //Even
val = builder->CreateXor(val, getInteger(1, 1));
c_SP(stack_diff);
if (on_stack)
set_top_value(val, T_BOOLEAN);
return val;
}
//Else fallthrough: deprecated Kill(1)
case 0x85: //Kill
case 0x87: //Rmdir, deprecated
if (extra == 0) extra = digit - 0x85;
codegen_stack
SUBR_CODE(SUBR_kill)
case 0x8A: //Link, deprecated -> IsNan() & IsInf
if (extra != 0){
codegen_value
if (extra == 1){
//IsNan
llvm::Value* res = builder->CreateICmpNE(builder->CreateCall(get_global_function(__isnan, 'i', "d"), param[0]), getInteger(32, 0));
c_SP(stack_diff);
if (on_stack)
set_top_value(res, T_BOOLEAN);
return res;
} else if (extra == 2){
//IsInf
llvm::Value* res = builder->CreateCall(get_global_function(__isinf, 'i', "d"), param[0]);
c_SP(stack_diff);
if (on_stack)
set_top_value(res, T_INTEGER);
return res;
}
}
//Else fallthrough: deprecated Link
case 0x88: //Move
case 0x89: //Copy, deprecated
if (extra == 0) extra = digit - 0x88;
codegen_stack
SUBR_CODE(SUBR_move)
case 0x8B: //Exist
codegen_stack
SUBR_CODE(SUBR_exist)
case 0x8C: //Access
codegen_stack
SUBR_CODE(SUBR_access)
case 0x8D: //Stat
codegen_stack
SUBR_CODE(SUBR_stat)
case 0x8E: //Dfree
codegen_stack
SUBR(SUBR_dfree)
case 0x8F: //Temp
codegen_stack
SUBR_CODE(SUBR_temp)
case 0x90: //IsDir
codegen_stack
SUBR_CODE(SUBR_isdir)
case 0x91: //Dir
codegen_stack
SUBR_CODE(SUBR_dir)
case 0x92: //RDir
codegen_stack
SUBR_CODE(SUBR_rdir)
case 0x93: //Exec
codegen_stack
SUBR_CODE(SUBR_exec)
case 0x94: //Alloc
codegen_stack
SUBR_CODE(SUBR_alloc)
case 0x95: //Free
codegen_stack
SUBR(SUBR_free)
case 0x96: //Realloc
codegen_stack
SUBR_CODE(SUBR_realloc)
case 0x97: //StrPtr
codegen_stack
SUBR_CODE(SUBR_strptr)
case 0x98: { //Sleep
codegen_value
store_element(temp_2longs, 0, builder->CreateFPToSI(param[0], LONG_TYPE));
llvm::Value* fabs_ret = builder->CreateCall(get_global_function(fabs, 'd', "d"), param[0]);
llvm::Value* floor_ret = builder->CreateCall(get_global_function(floor, 'd', "d"), fabs_ret);
llvm::Value* nsec = builder->CreateFMul(builder->CreateFSub(fabs_ret, floor_ret), getFloat(1000000000.0));
store_element(temp_2longs, 1, builder->CreateFPToSI(nsec, LONG_TYPE));
llvm::BasicBlock *BB1 = create_bb("nanosleep_loop"), *BB2 = create_bb("nanosleep_cont");
llvm::Value* ptr = builder->CreateBitCast(temp_2longs, llvmType(getInt8PtrTy));
builder->CreateBr(BB1);
builder->SetInsertPoint(BB1);
llvm::Value* res = builder->CreateCall2(get_global_function(nanosleep, 'i', "pp"), ptr, ptr);
builder->CreateCondBr(builder->CreateICmpSLT(res, getInteger(32, 0)), BB1, BB2);
builder->SetInsertPoint(BB2);
if (on_stack)
push_value(NULL, T_VOID);
return NULL;
}
case 0x99: { //VarPtr
auto pie = (PushIntegerExpression*)args[0];
llvm::Value* ret;
ushort op = pie->i;
int index = op & 0xFF;
if ((op & 0xFF00) == C_PUSH_LOCAL){
CLASS_LOCAL* var = &FP->local[index];
TYPE t = ctype_to_type(&var->type);
if (TYPE_is_string(t)){
ret = builder->CreateGEP(load_element(locals[index], 1), to_target_int(load_element(locals[index], 2)));
} else {
ret = builder->CreateBitCast(locals[index], llvmType(getInt8PtrTy));
}
} else if ((op & 0xF800) == C_PUSH_DYNAMIC) {
CLASS_VAR* var = &CP->load->dyn[op & 0x7FF];
ret = builder->CreateGEP(current_op, getInteger(TARGET_BITS, var->pos));
} else if ((op & 0xF800) == C_PUSH_STATIC) {
CLASS_VAR* var = &CP->load->stat[op & 0x7FF];
ret = builder->CreateGEP(get_global((void*)CP->stat, llvmType(getInt8Ty)), getInteger(TARGET_BITS, var->pos));
}
if (on_stack)
push_value(ret, T_POINTER);
return ret;
}
case 0x9A: //Collection
codegen_stack
SUBR_CODE(SUBR_collection)
case 0x9B: //Tr
codegen_stack
SUBR(SUBR_tr)
case 0x9C: //Quote, Shell, Html
codegen_stack
SUBR_CODE(SUBR_quote)
case 0x9D: //Unquote
codegen_stack
SUBR_CODE(SUBR_unquote)
case 0x9E: //MkInteger, ...
codegen_value
if (extra <= T_BYTE){
llvm::Value* offset = builder->CreateShl(builder->CreateZExt(param[0], LONG_TYPE), getInteger(TARGET_BITS, 1));
llvm::Value* ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING),
builder->CreateGEP(builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)&STRING_char_string[0]), llvmType(getInt8PtrTy)), offset),
getInteger(32, 0), getInteger(32, 1));
c_SP(stack_diff);
if (on_stack)
set_top_value(ret, T_CSTRING);
return ret;
} else {
static const int sizes[] = {0, 1, 1, 2, 4, 8, 4, 8, 8, 0, 0, TARGET_BITS/8};
c_SP(stack_diff);
llvm::Value* stack_top = get_value_on_top_addr();
builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"),
builder->CreateBitCast(stack_top, llvmType(getInt8PtrTy)),
get_nullptr(),
getInteger(32, sizes[extra]));
llvm::Value* ret = read_value(stack_top, T_STRING);
llvm::Value* ptr = extract_value(ret, 1);
borrow_string_no_nullcheck(ptr);
builder->CreateStore(param[0], builder->CreateBitCast(ptr, pointer_t(param[0]->getType())));
return ret;
}
break;
case 0x9F: //Ptr
codegen_stack
SUBR_CODE(SUBR_ptr)
default: assert(false && "Subr not implemented yet");
}
return ret_top_stack(type, on_stack);
}
void SubrExpression::codegen_on_stack(){
codegen_get_value();
}
void stack_corrupted_abort(){
fprintf(stderr, "Stack became corrupted in a JIT function. Please make a bug report.\n");
abort();
}
void NopExpression::codegen(){
/*builder->CreateCall4(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)"Nu: %s %p %p\n", llvmType(getInt8Ty)),
get_global((void*)buf, llvmType(getInt8Ty)),
read_global((void*)&SP),
read_global((void*)&BP));
builder->CreateCall4(get_global_function_vararg(printf, 'v', "p"),
get_global((void*)"Nu: %d %p %p\n", llvmType(getInt8Ty)),
getInteger(32, FP->n_local + FP->n_ctrl),
read_global((void*)&SP),
read_global((void*)&BP));*/
if (test_stack){
#ifndef GOSUB_ON_STACK
llvm::Value* sp = read_global((void*)&SP);
llvm::Value* bp = read_global((void*)&BP);
bp = builder->CreateGEP(bp, getInteger(TARGET_BITS, sizeof(VALUE)*(FP->n_local+FP->n_ctrl)));
#else
llvm::Value* sp = builder->CreateBitCast(read_global((void*)&SP), pointer_t(value_type));
llvm::Value* bp = builder->CreateLoad(gp);
#endif
gen_if_noreturn(builder->CreateICmpNE(bp, sp), [&](){
builder->CreateCall(get_global_function(stack_corrupted_abort, 'v', ""));
builder->CreateUnreachable();
});
}
}
void ProfileLineExpression::codegen(){
gen_if(builder->CreateICmpNE(read_global((void*)&EXEC_profile_instr, llvmType(getInt8Ty)), getInteger(8, false)), [&](){
builder->CreateCall3(get_global_function_jif(DEBUG_Profile_Add, 'v', "ppp"),
get_global((void*)CP, llvmType(getInt8Ty)),
get_global((void*)FP, llvmType(getInt8Ty)),
get_global((void*)pc, llvmType(getInt8Ty)));
});
}
void StopEventExpression::codegen(){
builder->CreateStore(getInteger(8, true), get_global((void*)&GAMBAS_StopEvent, llvmType(getInt8Ty)));
}
void ReturnExpression::codegen(){
auto normal_return = [&](){
llvm::Value* ret;
if (retval){
ret = retval->codegen_get_value();
if (retval->on_stack)
c_SP(-1);
} else {
ret = get_default(type);
}
store_value(get_global((void*)RP), ret, type);
if (in_try)
builder->CreateCall(get_global_function(JR_end_try, 'v', "p"),
create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, 0));
if (EC != NULL){
gen_if(builder->CreateXor(builder->CreateLoad(temp_got_error2), getInteger(1, 1)), [&](){
llvm::Value* call = builder->CreateCall(get_global_function(JR_end_try, 'v', "p"),
create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0));
if (llvm::Instruction* inst = llvm::dyn_cast<llvm::Instruction>(call)){
llvm::Value* arr[1] = {getInteger(32, 1)};
inst->setMetadata("large_end_try", llvm::MDNode::get(llvm_context, arr));
}
}, "return_in_large_try");
}
return_points.push_back(builder->GetInsertBlock());
};
if (ngosubs == 0 || kind > 0){
normal_return();
} else {
gen_if_noreturn(builder->CreateICmpEQ(builder->CreateLoad(gosub_return_point), getInteger(16, 0)), [&](){
normal_return();
});
//Else return in Gosub
gosub_return_points.push_back(builder->GetInsertBlock());
}
builder->SetInsertPoint(create_bb("dummy"));
}
void QuitExpression::codegen(){
if (quitval){
llvm::Value* val = quitval->codegen_get_value();
if (quitval->on_stack)
c_SP(-1);
builder->CreateStore(val, get_global((void*)&EXEC_quit_value, llvmType(getInt8Ty)));
}
builder->CreateCall(get_global_function_jif(EXEC_quit, 'v', ""));
builder->CreateUnreachable();
builder->SetInsertPoint(create_bb("dummy"));
}
static void debug_print_line(){
for (int i = 1; i < 10; i++)
fputs("--------", stderr);
fputc('\n', stderr);
}
static void run_optimizations(){
bool changed = true;
while(changed){
llvm::FunctionPassManager FPM(M);
llvm::PassManager MPM;
llvm::PassManagerBuilder PMB;
PMB.OptLevel = 2;
PMB.SizeLevel = 1;
PMB.populateFunctionPassManager(FPM);
PMB.populateModulePassManager(MPM);
//FPM.add(createGambasPass());
FPM.doInitialization();
FPM.run(*llvm_function);
FPM.doFinalization();
MPM.run(*M);
llvm::FunctionPass* pass = createGambasPass();
changed = pass->runOnFunction(*llvm_function);
delete pass;
}
}
template <typename T>
static void do_search(llvm::BasicBlock* block, T func){
std::queue<llvm::BasicBlock*> q;
std::set<llvm::BasicBlock*> vis;
q.push(block);
vis.insert(block);
while(!q.empty()){
llvm::BasicBlock* BB = q.front(); q.pop();
for(llvm::BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ){
if (func(I++))
goto next_block_in_queue;
}
for(llvm::succ_iterator SI = succ_begin(BB), E = succ_end(BB); SI != E; ++SI){
if (vis.count(*SI) == 0){
q.push(*SI);
vis.insert(*SI);
}
}
next_block_in_queue:;
}
}
static void fix_setjmp(llvm::BasicBlock* catch_block, llvm::BasicBlock* try_block, const char* end_try_string){
std::set<llvm::AllocaInst*> vars;
std::set<llvm::AllocaInst*> volatile_vars;
std::vector<llvm::StoreInst*> stores;
do_search(try_block, [&](llvm::Value* value){
if (llvm::StoreInst* si = llvm::dyn_cast<llvm::StoreInst>(value)){
if (llvm::AllocaInst* ai = llvm::dyn_cast<llvm::AllocaInst>(si->getPointerOperand())){
vars.insert(ai);
stores.push_back(si);
}
} else if (llvm::CallInst* ci = llvm::dyn_cast<llvm::CallInst>(value)){
if (ci->hasMetadata() && ci->getMetadata(end_try_string))
return true;
}
return false;
});
do_search(catch_block, [&](llvm::Value* value){
if (llvm::LoadInst* li = llvm::dyn_cast<llvm::LoadInst>(value)){
if (llvm::AllocaInst* ai = llvm::dyn_cast<llvm::AllocaInst>(li->getPointerOperand())){
if (vars.count(ai) != 0){
li->setVolatile(true);
volatile_vars.insert(ai);
}
}
}
return false;
});
for(size_t i=0, e=stores.size(); i!=e; i++){
llvm::StoreInst* si = stores[i];
llvm::AllocaInst* ai = llvm::dyn_cast<llvm::AllocaInst>(si->getPointerOperand());
if (volatile_vars.count(ai) != 0){
si->setVolatile(true);
}
}
}
static void fix_setjmps(){
if (!has_tries)
return;
if (EC != NULL){
//There is a Catch/Finally in the function, so a setjmp is somewhere in the entry block
//and the first branch is a conditional branch: true -> catch part, false -> try part
llvm::BranchInst* br = llvm::dyn_cast<llvm::BranchInst>(entry_block->getTerminator());
assert(br && br->isConditional());
llvm::BasicBlock* catch_block = br->getSuccessor(0);
llvm::BasicBlock* try_block = br->getSuccessor(1);
fix_setjmp(catch_block, try_block, "large_end_try");
}
for(size_t i=0, e=try_blocks.size(); i!=e; i++){
llvm::BasicBlock* BB = try_blocks[i];
llvm::BranchInst* br = llvm::dyn_cast<llvm::BranchInst>(BB->getTerminator());
assert(br && br->isConditional());
llvm::BasicBlock* catch_block = br->getSuccessor(0);
llvm::BasicBlock* try_block = br->getSuccessor(1);
fix_setjmp(catch_block, try_block, "end_try");
}
}
struct DynamicAllocatedString {
char* data;
int len;
DynamicAllocatedString(const char* data, int len){
this->data = new char[len];
this->len = len;
memcpy(this->data, data, len);
}
~DynamicAllocatedString(){
delete[] data;
}
};
static std::vector<DynamicAllocatedString> extern_signature_strings;
static std::map<llvm::StringRef, void(*)(void*, void*)> extern_signatures;
static void func_extern_call_variant_vararg(void* return_value_addr, void* func_addr, int nargs, TYPE return_type){
std::map<llvm::StringRef, void(*)(void*, void*)>& signatures = extern_signatures;
char signature_string[nargs+sizeof(TYPE)];
*(TYPE*)&signature_string[nargs] = return_type;
for(int i=0; i<nargs; i++){
TYPE t = SP[-nargs+i].type;
if (t == T_VARIANT){
JIF.F_VALUE_undo_variant(&SP[-nargs+i]);
t = SP[-nargs+i].type;
}
signature_string[i] = TYPE_is_pure_object(t) ? T_OBJECT : t;
}
auto it = signatures.find(llvm::StringRef(signature_string, nargs+sizeof(TYPE)));
if (it == signatures.end()){
M = new llvm::Module("jit_mod_vararg_variant_extern", llvm_context);
if (TARGET_BITS == 64){
M->setTargetTriple("x86_64-pc-linux-gnu");
M->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64");
} else {
M->setTargetTriple("i386-unknown-linux-gnu");
M->setDataLayout("e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128");
}
EE->addModule(M);
static char buf[256] = "extern_func_caller_";
int eob = strlen("extern_func_caller_");
for(int i=0; i<nargs; i++)
buf[eob+i] = signature_string[i]+'A';
buf[eob+nargs] = '\0';
//func_addr, return_value_addr
llvm_function = llvm::cast<llvm::Function>(M->getOrInsertFunction(buf, get_function_type('v', "pp", true)));
llvm::Function::arg_iterator arg_it = llvm_function->arg_begin();
llvm::Value* func_addr_arg = arg_it++;
llvm::Value* return_value_addr_arg = arg_it;
entry_block = create_bb("entry");
builder = new llvm::IRBuilder<>(entry_block);
std::vector<llvm::Type*> ft;
std::vector<llvm::Value*> orig_args;
std::vector<llvm::Value*> func_args;
ft.resize(nargs);
orig_args.resize(nargs);
func_args.resize(nargs);
llvm::Value* SP_base = builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -nargs));
llvm::Value* SP_current = SP_base;
for(int i=0; i<nargs; i++){
ft[i] = extern_types[signature_string[i]];
orig_args[i] = read_value(SP_current, signature_string[i]);
func_args[i] = codegen_extern_manage_value(orig_args[i], SP[-nargs+i].type);
if (i != nargs-1)
SP_current = builder->CreateGEP(SP_current, getInteger(TARGET_BITS, 1));
}
//Call function
llvm::Type* function_type = llvm::FunctionType::get(extern_types[return_type > T_OBJECT ? T_OBJECT : return_type], ft, true);
llvm::Value* call_function = builder->CreateBitCast(func_addr_arg, pointer_t(function_type));
llvm::Value* ret = builder->CreateCall(call_function, func_args);
//Manage return value
ret = codegen_extern_manage_return_value(ret, return_type);
if (return_type != T_VOID)
builder->CreateStore(ret, builder->CreateBitCast(return_value_addr_arg, pointer_t(TYPE_llvm(return_type))));
//Release arguments
for(int i=nargs; i --> 0; ){
release(orig_args[i], signature_string[i]);
c_SP(-1);
}
builder->CreateRetVoid();
//M->dump();
llvm::verifyModule(*M);
llvm::FunctionPassManager FPM(M);
llvm::PassManager MPM;
llvm::PassManagerBuilder PMB;
PMB.OptLevel = 2;
PMB.SizeLevel = 1;
PMB.populateFunctionPassManager(FPM);
PMB.populateModulePassManager(MPM);
FPM.doInitialization();
FPM.run(*llvm_function);
FPM.doFinalization();
MPM.run(*M);
//Print out the code after optimization
if (MAIN_debug){
debug_print_line();
fprintf(stderr, "gb.jit: dumping vararg extern call function\n");
debug_print_line();
M->dump();
debug_print_line();
fputc('\n', stderr);
}
void (*fn)(void*, void*) = (void(*)(void*, void*))EE->getPointerToFunction(llvm_function);
delete builder;
llvm_function->deleteBody();
mappings.clear();
extern_signature_strings.emplace_back((char*)signature_string, nargs+sizeof(TYPE));
signatures.insert(std::pair<llvm::StringRef, void(*)(void*, void*)>(llvm::StringRef(extern_signature_strings.back().data, nargs+sizeof(TYPE)), fn));
(*fn)(func_addr, return_value_addr);
} else {
(*(it->second))(func_addr, return_value_addr);
}
}
static void func_void(){
RP->type = T_VOID;
JIF.F_EXEC_leave_keep();
}
static void func_boolean(){
RP->type = T_BOOLEAN;
RP->_boolean.value = 0;
JIF.F_EXEC_leave_keep();
}
static void func_byte(){
RP->type = T_BYTE;
RP->_byte.value = 0;
JIF.F_EXEC_leave_keep();
}
static void func_short(){
RP->type = T_SHORT;
RP->_short.value = 0;
JIF.F_EXEC_leave_keep();
}
static void func_integer(){
RP->type = T_INTEGER;
RP->_integer.value = 0;
JIF.F_EXEC_leave_keep();
}
static void func_long(){
RP->type = T_LONG;
RP->_long.value = 0;
JIF.F_EXEC_leave_keep();
}
static void func_single(){
RP->type = T_SINGLE;
RP->_single.value = 0.0f;
JIF.F_EXEC_leave_keep();
}
static void func_float(){
RP->type = T_FLOAT;
RP->_float.value = 0.0;
JIF.F_EXEC_leave_keep();
}
static void func_date(){
RP->type = T_DATE;
RP->_date.date = 0;
RP->_date.time = 0;
JIF.F_EXEC_leave_keep();
}
static void func_string(){
RP->type = T_CSTRING;
RP->_string.addr = NULL;
RP->_string.start = 0;
RP->_string.len = 0;
JIF.F_EXEC_leave_keep();
}
static void func_pointer(){
RP->type = T_POINTER;
RP->_pointer.value = NULL;
JIF.F_EXEC_leave_keep();
}
static void func_variant(){
RP->type = T_VARIANT;
RP->_variant.vtype = T_NULL;
JIF.F_EXEC_leave_keep();
}
static void func_object(){
RP->type = T_OBJECT;
RP->_object.object = NULL;
JIF.F_EXEC_leave_keep();
}
void JIT_codegen(){
if (FP->type <= T_OBJECT && all_statements.size() == 1){
if (auto re = dyn_cast<ReturnExpression>(all_statements.front()->expr)){
if (re->retval == NULL){
//Standard empty function
delete all_statements.front();
all_statements.clear();
static void (* const funcs[17])(void) = {func_void, func_boolean, func_byte, func_short, func_integer,
func_long, func_single, func_float, func_date, func_string, func_string,
func_pointer, func_variant, NULL, NULL, NULL, func_object};
CP->jit_functions[EXEC.index] = funcs[FP->type];
return;
}
}
}
static bool inited = false;
if (!inited){
llvm_init();
inited = true;
}
M = new llvm::Module("jit_mod", llvm_context);
if (TARGET_BITS == 64){
M->setTargetTriple("x86_64-pc-linux-gnu");
M->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64");
} else {
M->setTargetTriple("i386-unknown-linux-gnu");
M->setDataLayout("e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128");
}
if (EE)
EE->addModule(M);
else
EE = llvm::EngineBuilder(M)/*.setOptLevel(llvm::CodeGenOpt::Aggressive)*/.create();
static int counter = 0;
char buf[256];
sprintf(buf, "func_%d_%s_%d", counter++, CP->name, EXEC.index);
llvm_function = llvm::cast<llvm::Function>(M->getOrInsertFunction(buf, get_function_type('v', "")));
entry_block = create_bb("entry");
builder = new llvm::IRBuilder<>(entry_block);
//builder->CreateCall(get_global_function((void*)&puts, 'v', "p"), get_global((void*)"hej", llvmType(getInt8Ty)));
//builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"enter %d\n", llvmType(getInt8Ty)), getInteger(32, counter-1));
current_op = OP == NULL ? get_nullptr() : read_global((void*)&OP);
temp_value = builder->CreateAlloca(value_type);
temp_value2 = builder->CreateAlloca(value_type);
temp_voidptr = builder->CreateAlloca(llvmType(getInt8PtrTy));
temp_int = builder->CreateAlloca(llvmType(getInt32Ty));
temp_double = builder->CreateAlloca(llvmType(getDoubleTy));
temp_date = builder->CreateAlloca(date_type);
temp_2longs = builder->CreateAlloca(two_longs_type);
temp_errcontext1 = builder->CreateAlloca(llvm::ArrayType::get(llvmType(getInt8Ty), sizeof(ERROR_CONTEXT)));
temp_errcontext2 = builder->CreateAlloca(llvm::ArrayType::get(llvmType(getInt8Ty), sizeof(ERROR_CONTEXT)));
temp_got_error = builder->CreateAlloca(llvmType(getInt1Ty));
temp_got_error2 = builder->CreateAlloca(llvmType(getInt1Ty));
if (ngosubs != 0){
//temp_gosub_stack = builder->CreateAlloca(llvm::ArrayType::get(llvmType(getInt8PtrTy), ngosubs + 100));
/*temp_num_gosubs_on_stack = builder->CreateAlloca(llvmType(getInt32Ty));
builder->CreateStore(getInteger(32, 0), temp_num_gosubs_on_stack);*/
#ifndef GOSUB_ON_STACK
builder->CreateCall3(get_global_function(GB.NewArray, 'v', "pii"),
get_global(&GP, llvmType(getInt8Ty)), getInteger(32, sizeof(STACK_GOSUB)), getInteger(32, 0));
#endif
/*gosub_return_point = builder->CreateAlloca(llvmType(getInt8PtrTy));
builder->CreateStore(get_nullptr(), gosub_return_point);*/
gosub_return_point = builder->CreateAlloca(llvmType(getInt16Ty));
builder->CreateStore(getInteger(16, 0), gosub_return_point);
}
//For GoSubs and JR_try_unwind
gp = builder->CreateAlloca(pointer_t(value_type));
builder->CreateStore(builder->CreateGEP(read_global((void*)&BP, pointer_t(value_type)), getInteger(TARGET_BITS, FP->n_local + FP->n_ctrl)), gp);
has_tries = false;
try_blocks.clear();
init_locals();
codegen_statements();
finish_gosub_returns();
create_return();
insert_pending_branches();
//puts("Dump:");
//Print out the code before optimization
//M->dump();
llvm::verifyModule(*M);
for(size_t i=0, e=all_statements.size(); i!=e; i++)
delete all_statements[i];
all_statements.clear();
fix_setjmps();
run_optimizations();
//Print out the code after optimization
if (MAIN_debug){
debug_print_line();
fprintf(stderr, "gb.jit: dumping function %s.", CP->name);
if (FP->debug)
fprintf(stderr, "%s:\n", FP->debug->name);
else
fprintf(stderr, "%d:\n", EXEC.index);
debug_print_line();
M->dump();
debug_print_line();
fputc('\n', stderr);
}
void (*fn)(void) = (void(*)(void))EE->getPointerToFunction(llvm_function);
delete builder;
llvm_function->deleteBody();
CP->jit_functions[EXEC.index] = fn;
/*if (CP->jit_data == NULL){
ALLOC_ZERO(&CP->jit_data, sizeof(llvm::ExecutionEngine*) * CP->load->n_func, "JIT_codegen");
}
((llvm::ExecutionEngine**)CP->jit_data)[EXEC.index] = EE;*/
mappings.clear();
//variable_mappings.clear();
}
static void JIT_cleanup(llvm::ExecutionEngine** EE, int n_func){
/*for(int i=0; i<n_func; i++){
if (EE[i]){
EE[i]->freeMachineCodeForFunction(EE[i]->FindFunctionNamed("func"));
delete EE[i];
}
}*/
}
void JIT_end(){
delete EE;
llvm::llvm_shutdown();
}
///DEBUG
void print_type(llvm::Value* v){
v->getType()->print(llvm::outs());
puts("");
}
void print_expr_type(Expression* expr){
puts(typeid(*expr).name());
}