Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for record extension #61

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
# Oberon
*.smb
*.sym
**/.obnc/

# macOS
*.DS_Store
Expand Down
34 changes: 17 additions & 17 deletions src/analyzer/LambdaLifter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void LambdaLifter::visit(ModuleNode &node) {

void LambdaLifter::visit(ProcedureNode &node) {
env_ = nullptr;
level_ = node.getLevel();
scope_ = node.getScope();
path_.push_back(node.getIdentifier()->name());
bool is_super = node.getProcedureCount();
if (is_super) /* super procedure */ {
Expand All @@ -55,20 +55,20 @@ void LambdaLifter::visit(ProcedureNode &node) {
auto ident = make_unique<IdentDef>(var->getIdentifier()->name());
fields.push_back(make_unique<FieldNode>(EMPTY_POS, std::move(ident), var->getType()));
}
auto type = context_->getOrInsertRecordType(EMPTY_POS, EMPTY_POS, std::move(fields));
auto type = context_->getOrInsertRecordType(EMPTY_POS, EMPTY_POS, nullptr, std::move(fields));
auto decl = make_unique<TypeDeclarationNode>(EMPTY_POS, std::move(identifier), type);
decl->setModule(module_);
decl->setLevel(module_->getLevel() + 1);
decl->setScope(module_->getScope() + 1);
module_->types().push_back(std::move(decl));
// insert an additional formal parameter to the sub-procedures of the procedure to pass its environment
for (auto &proc : node.procedures()) {
auto param = make_unique<ParameterNode>(EMPTY_POS, make_unique<Ident>(SUPER_), type, true);
param->setLevel(proc->getLevel() + 1);
param->setScope(proc->getScope() + 1);
proc->getType()->parameters().push_back(std::move(param));
}
// create local variable to manage for the procedure's environment (this)
auto var = make_unique<VariableDeclarationNode>(EMPTY_POS, make_unique<IdentDef>(THIS_), type);
var->setLevel(level_ + 1);
var->setScope(scope_ + 1);
env_ = var.get();
// initialize the procedure's environment (this)
for (size_t i = 0; i < node.getType()->parameters().size(); i++) {
Expand Down Expand Up @@ -97,7 +97,7 @@ void LambdaLifter::visit(ProcedureNode &node) {
}
}
// write updates back to super-procedure's environment (super := this.super)
if (level_ > SymbolTable::MODULE_LEVEL) /* neither root, nor leaf procedure */ {
if (scope_ > SymbolTable::MODULE_SCOPE) /* neither root, nor leaf procedure */ {
auto param = findParameter(SUPER_, node.getType()->parameters());
if (param) {
auto lhs = make_unique<QualifiedExpression>(param);
Expand All @@ -122,17 +122,17 @@ void LambdaLifter::visit(ProcedureNode &node) {
}
// TODO remove unnecessary local variables
// node.removeVariables(1, node.getVariableCount());
} else if (level_ > SymbolTable::MODULE_LEVEL) /* leaf procedure */ {
} else if (scope_ > SymbolTable::MODULE_SCOPE) /* leaf procedure */ {
if ((env_ = findParameter(SUPER_, node.getType()->parameters()))) {
for (size_t i = 0; i < node.statements()->getStatementCount(); i++) {
node.statements()->getStatement(i)->accept(*this);
}
}
}
if (level_ > SymbolTable::MODULE_LEVEL) {
level_ = module_->getLevel() + 1;
node.setLevel(level_);
level_++;
if (scope_ > SymbolTable::MODULE_SCOPE) {
scope_ = module_->getScope() + 1;
node.setScope(scope_);
scope_++;
for (auto &param : node.getType()->parameters()) {
param->accept(*this);
}
Expand Down Expand Up @@ -161,17 +161,17 @@ ParameterNode *LambdaLifter::findParameter(string name, vector<unique_ptr<Parame
void LambdaLifter::visit(ImportNode &) {}

void LambdaLifter::visit(ConstantDeclarationNode &node) {
node.setLevel(level_);
node.setScope(scope_);
}

void LambdaLifter::visit(FieldNode &) {}

void LambdaLifter::visit(ParameterNode &node) {
node.setLevel(level_);
node.setScope(scope_);
}

void LambdaLifter::visit(VariableDeclarationNode &node) {
node.setLevel(level_);
node.setScope(scope_);
}

// TODO copy-paste from visit(ProcedureNodeReference &)
Expand All @@ -183,8 +183,8 @@ void LambdaLifter::visit(QualifiedStatement &node) {
void LambdaLifter::visit(QualifiedExpression &node) {
auto decl = node.dereference();
selectors(decl->getType(), node.selectors());
if (decl->getLevel() == SymbolTable::MODULE_LEVEL ||
(env_->getIdentifier()->name() == SUPER_ && env_->getLevel() == decl->getLevel())) {
if (decl->getScope() == SymbolTable::MODULE_SCOPE ||
(env_->getIdentifier()->name() == SUPER_ && env_->getScope() == decl->getScope())) {
// global variable or local variable in leaf procedure
return;
}
Expand Down Expand Up @@ -269,7 +269,7 @@ void LambdaLifter::visit(SetExpressionNode &node) {
}

void LambdaLifter::visit(TypeDeclarationNode &node) {
node.setLevel(level_);
node.setScope(scope_);
}

void LambdaLifter::visit(ArrayTypeNode &) {}
Expand Down
4 changes: 2 additions & 2 deletions src/analyzer/LambdaLifter.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class LambdaLifter final : public Analysis, private NodeVisitor {
ASTContext *context_;
ModuleNode *module_;
DeclarationNode *env_;
unsigned int level_;
unsigned int scope_;
vector<string> path_;

static const string THIS_;
Expand Down Expand Up @@ -86,7 +86,7 @@ class LambdaLifter final : public Analysis, private NodeVisitor {
static bool envFieldResolver(QualifiedExpression *, const string &, TypeNode *);

public:
explicit LambdaLifter(ASTContext *context) : context_(context), module_(), env_(), level_(), path_() { };
explicit LambdaLifter(ASTContext *context) : context_(context), module_(), env_(), scope_(), path_() { };
~LambdaLifter() override = default;

void run(Logger &, Node *) override;
Expand Down
70 changes: 48 additions & 22 deletions src/codegen/llvm/LLVMIRBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,22 @@ using std::vector;

LLVMIRBuilder::LLVMIRBuilder(CompilerConfig &config, LLVMContext &builder, Module *module) :
NodeVisitor(), config_(config), logger_(config_.logger()), builder_(builder), module_(module),
value_(), values_(), types_(), hasArray_(), functions_(), strings_(), deref_ctx(), level_(0),
function_(), attrs_(AttrBuilder(builder)) {
value_(), values_(), types_(), leafTypes_(), hasArray_(), functions_(), strings_(), deref_ctx(),
scope_(0), function_(), attrs_(AttrBuilder(builder)) {
attrs_
.addAttribute(Attribute::NoInline)
.addAttribute(Attribute::NoUnwind)
.addAttribute(Attribute::OptimizeNone)
.addAttribute(Attribute::NoInline)
.addAttribute(Attribute::NoUnwind)
.addAttribute(Attribute::OptimizeNone)
#ifndef _LLVM_LEGACY
.addAttribute(Attribute::getWithUWTableKind(builder, UWTableKind::Default))
.addAttribute(Attribute::getWithUWTableKind(builder, UWTableKind::Default))
#endif
;
;
#ifndef __MINGW32__
if (!config_.hasFlag(Flag::NO_STACK_PROTECT)) {
attrs_.addAttribute(Attribute::StackProtect);
}
#endif
recordTdTy_ = StructType::create(builder_.getContext(), {builder_.getPtrTy(), builder_.getInt32Ty()});
}

void LLVMIRBuilder::build(ASTContext *ast) {
Expand Down Expand Up @@ -68,7 +69,7 @@ void LLVMIRBuilder::visit(ModuleNode &node) {
function_ = ::cast<Function>(body.getCallee());
auto entry = BasicBlock::Create(builder_.getContext(), "entry", function_);
builder_.SetInsertPoint(entry);
level_ = node.getLevel() + 1;
scope_ = node.getScope() + 1;
// generate code to initialize imports
for (auto &import : node.imports()) {
import->accept(*this);
Expand Down Expand Up @@ -127,7 +128,7 @@ void LLVMIRBuilder::visit(ProcedureNode &node) {
arrayInitializers(var->getType());
values_[var] = value_;
}
level_ = node.getLevel() + 1;
scope_ = node.getScope() + 1;
node.statements()->accept(*this);
if (node.getType()->getReturnType() == nullptr) {
builder_.CreateRetVoid();
Expand Down Expand Up @@ -186,19 +187,25 @@ void LLVMIRBuilder::arrayInitializers(TypeNode *base, TypeNode *type, vector<Val
builder_.SetInsertPoint(tail);
}
} else if (type->isRecord()) {
// TODO initialize base types
auto record_t = dynamic_cast<RecordTypeNode *>(type);
// skip the first field (type descriptor tag) of a leaf record type
indices.push_back(builder_.getInt32(1));
// check all record fields for initialization
for (size_t i = 0; i < record_t->getFieldCount(); ++i) {
indices.push_back(builder_.getInt32(i));
arrayInitializers(base, record_t->getField(i)->getType(), indices);
indices.pop_back();
}
indices.pop_back();
}
}

void LLVMIRBuilder::visit(ImportNode &node) {
std::string name = node.getModule()->name();
if (name == "SYSTEM") {
return; /* no initialization for pseudo modules */
// no initialization for pseudo modules
return;
}
auto type = FunctionType::get(builder_.getInt32Ty(), {});
auto fun = module_->getOrInsertFunction(name, type);
Expand All @@ -220,12 +227,12 @@ void LLVMIRBuilder::visit(QualifiedExpression &node) {
// If the qualified expression refers to a type, no code has to be generated.
return;
}
auto level = decl->getLevel();
if (level == 0 || level == 1) /* universe or global level */ {
auto scope = decl->getScope();
if (scope == 0 || scope == 1) /* universe or global scope */ {
value_ = values_[decl];
} else if (level == level_) /* same procedure level */ {
} else if (scope == scope_) /* same procedure scope */ {
value_ = values_[decl];
} else if (level > level_) /* parent procedure level */ {
} else if (scope > scope_) /* parent procedure scope */ {
logger_.error(node.pos(), "referencing variables of parent procedures is not yet supported.");
} else /* error */ {
logger_.error(node.pos(), "cannot reference variable of child procedure.");
Expand Down Expand Up @@ -306,12 +313,16 @@ TypeNode *LLVMIRBuilder::selectors(TypeNode *base, SelectorIterator start, Selec
// handle record field access
auto field = dynamic_cast<RecordField *>(sel)->getField();
auto record_t = dynamic_cast<RecordTypeNode *>(selector_t);
for (size_t pos = 0; pos < record_t->getFieldCount(); pos++) {
if (field == record_t->getField(pos)) {
indices.push_back(builder_.getInt32(pos));
break;
}
// skip the first field (type descriptor tag) of a leaf record type
indices.push_back(builder_.getInt32(1));
// navigate through the base records
unsigned current = field->getRecordType()->getLevel();
for (unsigned level = current; level < record_t->getLevel(); level++) {
indices.push_back(builder_.getInt32(0));
}
// access the field by its index (increase index at levels > 0)
indices.push_back(builder_.getInt32(current == 0 ? field->getIndex() : field->getIndex() + 1));
// output GEP up to the record field
value = processGEP(base, value, indices);
selector_t = field->getType();
base = selector_t;
Expand Down Expand Up @@ -1242,7 +1253,8 @@ LLVMIRBuilder::createNewCall(TypeNode *type, llvm::Value *param) {
// TODO remove next line (bit-cast) once non-opaque pointers are no longer supported
value_ = builder_.CreateBitCast(value_, getLLVMType(ptr));
Value *value = builder_.CreateStore(value_, param);
value_ = builder_.CreateLoad(builder_.getPtrTy(), param);
// TODO the following load is probably redundant as the address is already in `value_`
// value_ = builder_.CreateLoad(builder_.getPtrTy(), param);
arrayInitializers(base);
return value;
}
Expand Down Expand Up @@ -1578,10 +1590,10 @@ Value *LLVMIRBuilder::processGEP(TypeNode *base, Value *value, vector<Value *> &
return value;
}

Type* LLVMIRBuilder::getLLVMType(TypeNode *type) {
Type* LLVMIRBuilder::getLLVMType(TypeNode *type, bool leaf) {
Type* result = nullptr;
if (type == nullptr) {
result = builder_.getVoidTy();
return builder_.getVoidTy();
} else if (types_[type] != nullptr) {
result = types_[type];
} else if (type->getNodeType() == NodeType::array_type) {
Expand All @@ -1597,6 +1609,11 @@ Type* LLVMIRBuilder::getLLVMType(TypeNode *type) {
types_[type] = structTy;
vector<Type *> elemTys;
auto recordTy = dynamic_cast<RecordTypeNode *>(type);
// add field for base record
if (recordTy->isExtended()) {
elemTys.push_back(getLLVMType(recordTy->getBaseType(), false));
}
// add regular record fields
for (size_t i = 0; i < recordTy->getFieldCount(); i++) {
auto fieldTy = recordTy->getField(i)->getType();
elemTys.push_back(getLLVMType(fieldTy));
Expand Down Expand Up @@ -1641,6 +1658,15 @@ Type* LLVMIRBuilder::getLLVMType(TypeNode *type) {
logger_.error(type->pos(), "cannot map " + to_string(type->kind()) + " to LLVM intermediate representation.");
exit(1);
}
if (leaf && type->getNodeType() == NodeType::record_type) {
auto leafType = leafTypes_[type];
if (!leafType) {
leafType = StructType::create(builder_.getContext(), {builder_.getPtrTy(), result});
leafType->setName("record." + to_string(*type->getIdentifier()) + ".leaf");
leafTypes_[type] = leafType;
}
result = leafType;
}
return result;
}

Expand Down
6 changes: 4 additions & 2 deletions src/codegen/llvm/LLVMIRBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,18 @@ class LLVMIRBuilder final : private NodeVisitor {
Value *value_;
map<DeclarationNode*, Value*> values_;
map<TypeNode*, Type*> types_;
map<TypeNode*, StructType*> leafTypes_;
unordered_set<TypeNode *> hasArray_;
map<ProcedureNode*, Function*> functions_;
map<string, Constant*> strings_;
stack<bool> deref_ctx;
unsigned int level_;
unsigned int scope_;
Function *function_;
AttrBuilder attrs_;
ASTContext *ast_;
Type *recordTdTy_;

Type *getLLVMType(TypeNode *type);
Type *getLLVMType(TypeNode *type, bool = true);
MaybeAlign getLLVMAlign(TypeNode *type);

Value *processGEP(TypeNode *, Value *, vector<Value *> &);
Expand Down
4 changes: 2 additions & 2 deletions src/data/ast/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ ASTContext::getOrInsertArrayType(const FilePos &start, [[maybe_unused]] const Fi

RecordTypeNode *
ASTContext::getOrInsertRecordType(const FilePos &start, [[maybe_unused]] const FilePos &end,
vector<unique_ptr<FieldNode>> fields) {
auto type = make_unique<RecordTypeNode>(start, std::move(fields));
RecordTypeNode *base, vector<unique_ptr<FieldNode>> fields) {
auto type = make_unique<RecordTypeNode>(start, base, std::move(fields));
auto res = type.get();
record_ts_.push_back(std::move(type));
return res;
Expand Down
2 changes: 1 addition & 1 deletion src/data/ast/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class ASTContext {
[[deprecated]]
ArrayTypeNode *getOrInsertArrayType(const FilePos &, const FilePos &, unsigned, TypeNode *);
ArrayTypeNode *getOrInsertArrayType(const FilePos &, const FilePos &, unsigned, vector<unsigned>, vector<TypeNode *>);
RecordTypeNode *getOrInsertRecordType(const FilePos &, const FilePos &, vector<unique_ptr<FieldNode>>);
RecordTypeNode *getOrInsertRecordType(const FilePos &, const FilePos &, RecordTypeNode *, vector<unique_ptr<FieldNode>>);
PointerTypeNode *getOrInsertPointerType(const FilePos &, const FilePos &, TypeNode *);
ProcedureTypeNode *getOrInsertProcedureType(const FilePos &, const FilePos &,
vector<unique_ptr<ParameterNode>>, bool, TypeNode *);
Expand Down
27 changes: 21 additions & 6 deletions src/data/ast/DeclarationNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ void DeclarationNode::print(std::ostream &stream) const {
stream << *getIdentifier() << ": " << *getType();
}

unsigned int DeclarationNode::index() const {
return index_;
unsigned int DeclarationNode::seqId() const {
return seqId_;
}

void DeclarationNode::setLevel(unsigned int level) {
level_ = level;
void DeclarationNode::setScope(unsigned int scope) {
scope_ = scope;
}

unsigned int DeclarationNode::getLevel() const {
return level_;
unsigned int DeclarationNode::getScope() const {
return scope_;
}


Expand Down Expand Up @@ -81,6 +81,21 @@ void VariableDeclarationNode::accept(NodeVisitor& visitor) {
visitor.visit(*this);
}

void FieldNode::setRecordType(RecordTypeNode *parent) {
parent_ = parent;
}

void FieldNode::setIndex(unsigned index) {
index_ = index;
}

RecordTypeNode *FieldNode::getRecordType() const {
return parent_;
}

unsigned int FieldNode::getIndex() const {
return index_;
}

void FieldNode::accept(NodeVisitor& visitor) {
visitor.visit(*this);
Expand Down
Loading