Я реализую нетипизированным язык программирования, использующий LLVM для генерации внутреннего кода. Чтобы отслеживать текущий тип определенной переменной, я использую структуру StructTy_struct_datatype_t
который определяется как:
PointerTy_8 = PointerType::get(IntegerType::get(TheContext, 8), 0);
StructTy_struct_datatype_t = StructType::create(TheContext, "struct.datatype_t");
std::vector<Type *> StructTy_struct_datatype_t_fields;
StructTy_struct_datatype_t_fields.push_back(IntegerType::get(TheContext, 32));
StructTy_struct_datatype_t_fields.push_back(PointerTy_8);
// which represents the struct
typedef struct datatype_t {
int type; // holds an integer that tells me the type (1 = int, 2 = float, ...)
void* v; // holds a pointer to the actual value
} datatype_t;
Тогда предположим, что у меня есть такая функция
def function_add(a, b) {
return a + b;
}
Я хочу, чтобы эта функция была в состоянии принять
function_add(1, 1); // returns 2; (int)
function_add(1.0, 1.0); // returns 2.0 (float)
function_add("str1", "str2"); // returns "str1str2" (string)
Код, который обрабатывает двоичную операцию т.е. a + b
следует
Value* L = lhs_codegen_elements.back();
Value* R = rhs_codegen_elements.back();
if (!L || !R) {
logError("L or R are undefined");
return codegen;
}
AllocaInst* lptr_datatype = (AllocaInst*)((LoadInst*)L)->getPointerOperand();
AllocaInst* rptr_datatype = (AllocaInst*)((LoadInst*)R)->getPointerOperand();
ConstantInt* const_int32_0 = ConstantInt::get(TheContext, APInt(32, StringRef("0"), 10));
ConstantInt* const_int32_1 = ConstantInt::get(TheContext, APInt(32, StringRef("1"), 10));
GetElementPtrInst* lptr_type =
GetElementPtrInst::Create(StructTy_struct_datatype_t, lptr_datatype, {const_int32_0, const_int32_0}, "type");
GetElementPtrInst* rptr_type =
GetElementPtrInst::Create(StructTy_struct_datatype_t, rptr_datatype, {const_int32_0, const_int32_0}, "type");
GetElementPtrInst* lptr_v =
GetElementPtrInst::Create(StructTy_struct_datatype_t, lptr_datatype, {const_int32_0, const_int32_1}, "v");
GetElementPtrInst* rptr_v =
GetElementPtrInst::Create(StructTy_struct_datatype_t, rptr_datatype, {const_int32_0, const_int32_1}, "v");
LoadInst* lload_inst_type = load_inst_codegen(TYPE_INT, lptr_type);
LoadInst* rload_inst_type = load_inst_codegen(TYPE_INT, rptr_type);
LoadInst* lload_inst_v = load_inst_codegen(TYPE_VOID_POINTER, lptr_v);
LoadInst* rload_inst_v = load_inst_codegen(TYPE_VOID_POINTER, rptr_v);
CmpInst* cond1 =
new ICmpInst(ICmpInst::ICMP_EQ, lload_inst_type, ConstantInt::get(TheContext, APInt(32, TYPE_DOUBLE)));
Function* function_bb = dyn_cast<Function>(bb);
BasicBlock* label_if_then_double = BasicBlock::Create(TheContext, "if.then.double", function_bb);
BasicBlock* label_if_then_long = BasicBlock::Create(TheContext, "if.then.long", function_bb);
BranchInst* branch_inst = BranchInst::Create(label_if_then_double, label_if_else, cond1, bb);
L->dump(); // %load_inst = load %struct.datatype_t, %struct.datatype_t* %alloca_datatype_v, align 8
R->dump(); // %load_inst = load %struct.datatype_t, %struct.datatype_t* %alloca_datatype_v1, align 8
L->getType()->dump(); // %struct.datatype_t = type { i32, i8* }
R->getType()->dump(); // %struct.datatype_t = type { i32, i8* }
lload_inst_type->dump(); // %load_inst = load i32, i32* %type, align 4
rload_inst_type->dump(); // %load_inst = load i32, i32* %type, align 4
lload_inst_v->dump(); // %load_inst = load i8*, i8** %v, align 8
rload_inst_v->dump(); // %load_inst = load i8*, i8** %v, align 8
if (op == '+') {
// issue: how to take the decision without knowing the type lload_inst_v holds
BinaryOperator::Create(Instruction::FAdd, lload_inst_v, rload_inst_v, "add", label_if_then_double);
// or
BinaryOperator::Create(Instruction::Add, lload_inst_v, rload_inst_v, "add", label_if_then_long);
}
Так что проблема в том, что мне нужно знать, какой тип lload_inst_type
а также rload_inst_type
удерживайте, чтобы я мог переключать методы из LLVM API BinaryOperator::Create(Instruction::FAdd, ...)
за floats
а также BinaryOperator::Create(Instruction::Add, ...)
за ints
, например.
Тем не менее, я только что понял, что не могу понять значение AllocaInst
, LoadInst
при создании бэкэнда (по крайней мере, я не знаю, как это сделать).
Value*
во время выполнения? Если ваша система типов исходного языка не типизирована, ее нужно будет скрыть от LLVM, так как это тип IR. Вам нужно будет придумать способ отслеживания типа во время выполнения, может быть, какая-то система перечисленных тегированных объектов. Ваши вызовы функций должны будут проверить типы, передаваемые во время выполнения, и выбрать подходящую функцию для вызова.
LLVM не предоставляет никаких этих функций, это должно быть ответственностью системы типов времени исполнения вашего языка.
Других решений пока нет …