Add Struct and StructRef-types

This commit is contained in:
Sofia 2026-04-15 18:21:22 +03:00
parent 5fc58ea1c5
commit bca18b4f1b
7 changed files with 158 additions and 62 deletions

View File

@ -1,6 +1,8 @@
#ifndef BUILDER_H
#define BUILDER_H
#include <map>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/IRBuilder.h>
@ -11,6 +13,8 @@ namespace codegen {
std::unique_ptr<llvm::IRBuilder<>> builder;
llvm::BasicBlock* block;
};
typedef std::map<std::string, llvm::Type*> TypeMap;
}
#endif

View File

@ -32,13 +32,15 @@ namespace types {
if (target_ty->is_signed()) {
casts.push_back(CastDefinition{ source_ty, target_ty, allow_implicit,
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
return builder.builder->CreateSExtOrTrunc(value, target->codegen(builder), "cast");
codegen::TypeMap empty {};
return builder.builder->CreateSExtOrTrunc(value, target->codegen(builder, empty), "cast");
} });
}
else {
casts.push_back(CastDefinition{ source_ty, target_ty, allow_implicit,
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
return builder.builder->CreateZExtOrTrunc(value, target->codegen(builder), "cast");
codegen::TypeMap empty {};
return builder.builder->CreateZExtOrTrunc(value, target->codegen(builder, empty), "cast");
} });
}
}
@ -60,14 +62,6 @@ namespace types {
return {};
return CastDefinition{ casted_ty, target_ty, true,
[](codegen::Builder&, std::shared_ptr<Type>, llvm::Value* value) {
// auto ptr_ty = dynamic_cast<types::PointerType*>(target.get());
// std::vector<llvm::Value*> indices {};
// indices.push_back(llvm::ConstantInt::get(
// builder.builder->getInt32Ty(),
// 0
// ));
// return builder.builder->CreateGEP(ptr_ty->m_inner->codegen(builder), value, indices, "arraydecay");
return value;
} };
}

View File

@ -11,7 +11,7 @@
namespace codegen {
Scope Scope::with_lvalue() {
return Scope{ this->binops, this->casts, this->values, true };
return Scope{ this->binops, this->casts, this->structs, this->values, true };
}
}
@ -20,8 +20,8 @@ namespace AST {
return this->m_ty;
}
codegen::StackValue IntLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope&) {
auto ty = this->m_ty->codegen(builder);
codegen::StackValue IntLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto ty = this->m_ty->codegen(builder, scope.structs);
return codegen::StackValue{
llvm::ConstantInt::get(ty, this->m_value),
@ -56,7 +56,7 @@ namespace AST {
}
else {
return codegen::StackValue{
builder.builder->CreateLoad(stack_type->codegen(builder), global_str, "literal"),
builder.builder->CreateLoad(stack_type->codegen(builder, scope.structs), global_str, "literal"),
std::unique_ptr<types::Type>{stack_type},
};
}
@ -83,7 +83,7 @@ namespace AST {
return value->second;
}
else {
auto loaded = value->second.ty->load(builder, value->second.value);
auto loaded = value->second.ty->load(builder, value->second.value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
@ -160,7 +160,7 @@ namespace AST {
}
auto function = this->m_fn_expr->codegen(builder, scope);
auto value = builder.builder->CreateCall(llvm::dyn_cast<llvm::FunctionType>(function.ty->codegen(builder)), function.value, args, "call");
auto value = builder.builder->CreateCall(llvm::dyn_cast<llvm::FunctionType>(function.ty->codegen(builder, scope.structs)), function.value, args, "call");
return codegen::StackValue{
value,
*function.ty->return_type(),
@ -226,7 +226,7 @@ namespace AST {
codegen::StackValue DerefExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto value = this->m_expr->codegen(builder, scope);
if (value.ty->m_kind == types::TypeKind::Pointer) {
auto loaded = value.ty->load(builder, value.value);
auto loaded = value.ty->load(builder, value.value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
@ -267,7 +267,7 @@ namespace AST {
if (ptr_ty->m_inner->m_kind == types::TypeKind::Pointer) {
auto inner_ptr_ty = dynamic_cast<types::PointerType*>(ptr_ty->m_inner.get());
auto gep_value = builder.builder->CreateGEP(inner_ptr_ty->m_inner->codegen(builder), value.value, idx_list, "GEP");
auto gep_value = builder.builder->CreateGEP(inner_ptr_ty->m_inner->codegen(builder, scope.structs), value.value, idx_list, "GEP");
if (scope.is_lvalue) {
return codegen::StackValue{
gep_value,
@ -275,7 +275,7 @@ namespace AST {
};
}
else {
auto loaded = value.ty->load(builder, gep_value);
auto loaded = value.ty->load(builder, gep_value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
@ -287,7 +287,7 @@ namespace AST {
// Must be an array otherwise
auto arr_ty = dynamic_cast<types::ArrayType*>(ptr_ty->m_inner.get());
auto gep_value = builder.builder->CreateGEP(arr_ty->m_inner->codegen(builder), value.value, idx_list, "GEP");
auto gep_value = builder.builder->CreateGEP(arr_ty->m_inner->codegen(builder, scope.structs), value.value, idx_list, "GEP");
if (scope.is_lvalue) {
return codegen::StackValue{
gep_value,
@ -296,7 +296,7 @@ namespace AST {
}
else {
auto inner_ptr_ty = types::PointerType{ arr_ty->m_inner };
auto loaded = inner_ptr_ty.load(builder, gep_value);
auto loaded = inner_ptr_ty.load(builder, gep_value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
@ -304,7 +304,7 @@ namespace AST {
}
}
else {
auto gep_value = builder.builder->CreateGEP(ptr_ty->m_inner->codegen(builder), value.value, idx_list, "GEP");
auto gep_value = builder.builder->CreateGEP(ptr_ty->m_inner->codegen(builder, scope.structs), value.value, idx_list, "GEP");
if (scope.is_lvalue) {
return codegen::StackValue{
gep_value,
@ -312,7 +312,7 @@ namespace AST {
};
}
else {
auto loaded = value.ty->load(builder, gep_value);
auto loaded = value.ty->load(builder, gep_value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
@ -331,7 +331,7 @@ namespace AST {
}
codegen::StackValue ListInitializerExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto array_ptr = builder.builder->CreateAlloca(this->m_ty->codegen(builder));
auto array_ptr = builder.builder->CreateAlloca(this->m_ty->codegen(builder, scope.structs));
if (this->m_ty->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(this->m_ty.get());
@ -341,7 +341,7 @@ namespace AST {
std::vector<llvm::Value*> indices{};
indices.push_back(llvm::ConstantInt::get(builder.builder->getInt32Ty(), counter++));
auto gep = builder.builder->CreateGEP(
array_ty->m_inner->codegen(builder), array_ptr, indices, "GEP");
array_ty->m_inner->codegen(builder, scope.structs), array_ptr, indices, "GEP");
builder.builder->CreateStore(expr->codegen(builder, scope).value, gep);
}
@ -353,7 +353,7 @@ namespace AST {
return codegen::StackValue{ array_ptr, ptr_ty };
}
else {
auto loaded = ptr_ty->load(builder, array_ptr);
auto loaded = ptr_ty->load(builder, array_ptr, scope.structs);
return codegen::StackValue{ loaded.first, loaded.second };
}
}
@ -387,7 +387,7 @@ namespace AST {
builder.builder->SetInsertPoint(builder.block);
auto ty = this->m_type->codegen(builder);
auto ty = this->m_type->codegen(builder, scope.structs);
auto ptr = builder.builder->CreateAlloca(ty);
if (this->m_expr.has_value()) {
auto value = this->m_expr->get()->codegen(builder, scope);
@ -445,7 +445,7 @@ namespace AST {
auto fn_ty_ptr = std::shared_ptr<types::Type>{ new types::FunctionType{ ret_ty_ptr, param_ty_ptrs, this->m_is_vararg } };
auto fn_ty = fn_ty_ptr->codegen(builder);
auto fn_ty = fn_ty_ptr->codegen(builder, scope.structs);
auto function = llvm::Function::Create(
llvm::dyn_cast<llvm::FunctionType>(fn_ty),
llvm::GlobalValue::LinkageTypes::ExternalLinkage,
@ -476,7 +476,7 @@ namespace AST {
}
else {
builder.builder->SetInsertPoint(BB);
auto arg_ptr = builder.builder->CreateAlloca(param_ty_ptrs[counter]->codegen(builder));
auto arg_ptr = builder.builder->CreateAlloca(param_ty_ptrs[counter]->codegen(builder, scope.structs));
auto param_ty_ptr = std::shared_ptr<types::Type>{
new types::PointerType { param_ty_ptrs[counter]}
};
@ -506,7 +506,7 @@ namespace AST {
}
namespace types {
llvm::Type* FundamentalType::codegen(codegen::Builder& builder) {
llvm::Type* FundamentalType::codegen(codegen::Builder& builder, codegen::TypeMap&) {
switch (this->m_ty) {
case FundamentalTypeKind::Int:
return builder.builder->getInt32Ty();
@ -521,23 +521,44 @@ namespace types {
}
}
llvm::Type* FunctionType::codegen(codegen::Builder& builder) {
llvm::Type* FunctionType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
std::vector<llvm::Type*> params{};
for (auto& param : this->m_param_tys) {
params.push_back(param->codegen(builder));
params.push_back(param->codegen(builder, structs));
}
auto ret_ty = this->m_ret_ty->codegen(builder);
auto ret_ty = this->m_ret_ty->codegen(builder, structs);
return llvm::FunctionType::get(ret_ty, params, this->m_vararg);
}
llvm::Type* PointerType::codegen(codegen::Builder& builder) {
llvm::Type* PointerType::codegen(codegen::Builder& builder, codegen::TypeMap&) {
return llvm::PointerType::get(*builder.context, 0);
}
llvm::Type* ArrayType::codegen(codegen::Builder& builder) {
return llvm::ArrayType::get(this->m_inner->codegen(builder), this->m_size);
llvm::Type* ArrayType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
return llvm::ArrayType::get(this->m_inner->codegen(builder, structs), this->m_size);
}
llvm::Type* StructType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
if (this->m_fields) {
std::vector<llvm::Type*> fields{};
for (auto& field : *this->m_fields) {
fields.push_back(field.second->codegen(builder, structs));
}
auto ty = llvm::StructType::get(*builder.context, fields);
if (this->m_name)
ty->setName(*this->m_name);
return ty;
}
else {
return llvm::StructType::get(*builder.context);
}
}
llvm::Type* StructRef::codegen(codegen::Builder&, codegen::TypeMap& structs) {
return structs[this->m_name];
}
}

View File

@ -22,7 +22,9 @@ namespace codegen {
std::vector<types::BinopDefinition>& binops;
std::vector<types::CastDefinition>& casts;
TypeMap structs;
std::map<std::string, StackValue> values;
bool is_lvalue;
Scope with_lvalue();

View File

@ -124,6 +124,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
codegen::Scope cg_scope{
.binops = typecheck_state.binops,
.casts = typecheck_state.casts,
.structs = {},
.values = {},
.is_lvalue = false,
};

View File

@ -4,6 +4,7 @@
#include "types.h"
#include "binops.h"
#include "builder.h"
namespace types {
int operator_precedence(BinOp& op) {
@ -63,7 +64,7 @@ namespace types {
return false;
}
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr) {
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
auto self = std::make_shared<FundamentalType>(*this);
return std::pair(ptr, self);
}
@ -118,7 +119,7 @@ namespace types {
return this->m_ret_ty;
}
std::pair<llvm::Value*, std::shared_ptr<Type>> FunctionType::load(codegen::Builder&, llvm::Value* ptr) {
std::pair<llvm::Value*, std::shared_ptr<Type>> FunctionType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
auto self = std::make_shared<FunctionType>(*this);
return std::pair(ptr, self);
}
@ -133,9 +134,9 @@ namespace types {
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> PointerType::load(codegen::Builder& builder, llvm::Value* ptr) {
std::pair<llvm::Value*, std::shared_ptr<Type>> PointerType::load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) {
return std::pair(
builder.builder->CreateLoad(this->m_inner->codegen(builder), ptr),
builder.builder->CreateLoad(this->m_inner->codegen(builder, structs), ptr),
this->m_inner
);
}
@ -151,7 +152,7 @@ namespace types {
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> ArrayType::load(codegen::Builder&, llvm::Value* ptr) {
std::pair<llvm::Value*, std::shared_ptr<Type>> ArrayType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
auto self = std::make_shared<ArrayType>(*this);
return std::pair(ptr, self);
}
@ -160,18 +161,59 @@ namespace types {
return this->m_size * this->m_inner->size();
}
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2) {
// if (type1->m_kind == TypeKind::Array && type2->m_kind == TypeKind::Pointer) {
// auto ty1 = dynamic_cast<ArrayType*>(type1.get());
// auto ty2 = dynamic_cast<PointerType*>(type2.get());
// return types_equal(ty1->m_inner, ty2->m_inner);
// }
// else if (type1->m_kind == TypeKind::Pointer && type2->m_kind == TypeKind::Array) {
// auto ty1 = dynamic_cast<PointerType*>(type1.get());
// auto ty2 = dynamic_cast<ArrayType*>(type2.get());
// return types_equal(ty1->m_inner, ty2->m_inner);
// }
std::string StructType::formatted() {
std::stringstream out{ "" };
out << "struct ";
if (this->m_name) {
out << *this->m_name << " ";
}
if (this->m_fields) {
out << "{ ";
int counter = 0;
for (auto& field : *this->m_fields) {
if (counter++ > 0)
out << ", ";
out << field.first << ": " << field.second->formatted() << " ";
}
out << "}";
}
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> StructType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
auto self = std::make_shared<StructType>(*this);
return std::pair(ptr, self);
}
uint32_t StructType::size() {
uint32_t size{ 0 };
if (this->m_fields) {
for (auto& field : *this->m_fields) {
size += field.second->size();
}
}
return size;
}
std::string StructRef::formatted() {
std::stringstream out{ "" };
out << "struct(ref) " << this->m_name;
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> StructRef::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
auto self = std::make_shared<StructRef>(*this);
return std::pair(ptr, self);
}
uint32_t StructRef::size() {
return this->m_referred->size();
}
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2) {
if (type1->m_kind != type2->m_kind)
return false;

View File

@ -29,8 +29,8 @@ namespace types {
Type(TypeKind kind) : m_kind{ kind } {}
virtual ~Type() = default;
virtual std::string formatted() = 0;
virtual llvm::Type* codegen(codegen::Builder& builder) = 0;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) = 0;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) = 0;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) = 0;
virtual std::optional<std::shared_ptr<Type>> return_type();
virtual bool is_signed();
virtual uint32_t size() = 0;
@ -42,8 +42,8 @@ namespace types {
FundamentalType(FundamentalTypeKind kind) : Type(TypeKind::Fundamental), m_ty{ kind } {}
virtual ~FundamentalType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual bool is_signed() override;
virtual uint32_t size() override;
};
@ -62,8 +62,8 @@ namespace types {
}
virtual ~FunctionType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual std::optional<std::shared_ptr<Type>> return_type() override;
virtual uint32_t size() override;
};
@ -77,8 +77,8 @@ namespace types {
}
virtual ~PointerType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
};
@ -92,8 +92,40 @@ namespace types {
}
virtual ~ArrayType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
};
typedef std::pair<std::string, std::shared_ptr<types::Type>> StructField;
class StructType : public Type {
public:
std::optional<std::string> m_name;
std::optional<std::vector<StructField>> m_fields;
StructType(std::optional<std::string> name, std::optional<std::vector<StructField>> fields)
: Type(TypeKind::Array), m_name{ name }, m_fields{ fields } {
}
virtual ~StructType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
};
class StructRef : public Type {
public:
std::string m_name;
std::shared_ptr<types::Type> m_referred;
StructRef(std::string name, std::shared_ptr<types::Type> referred)
: Type(TypeKind::Array), m_name{ name }, m_referred{ referred } {
}
virtual ~StructRef() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
};