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

Record initialization with no designators #165

Merged
merged 8 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions include/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,17 @@ class RecordType : public Type {
/// `id` is not a member of the record type.
virtual std::unique_ptr<Type> MemberType(
const std::string& id) const noexcept = 0;
/// @note Every member in union shares the same offset 0.
/// @return The type offset in the record based on `id`.
/// @throw `std::runtime_error` if the `id` is not a member of the record.
virtual std::size_t OffsetOf(const std::string& id) const = 0;
/// @note Every member in union shares the same offset 0.
/// @return The type offset in the record based on `index`.
/// @throw `std::out_of_range` if the `index` is out of range.
virtual std::size_t OffsetOf(std::size_t index) const = 0;
/// @return The total number of members a record can hold.
/// @note For union type, there's at most one slot.
virtual std::size_t SlotCount() const noexcept = 0;
};

class StructType : public RecordType {
Expand All @@ -203,11 +214,13 @@ class StructType : public RecordType {
StructType(std::string id, std::vector<std::unique_ptr<Field>> fields)
: id_{std::move(id)}, fields_{std::move(fields)} {}

std::string id() // NOLINT(readability-identifier-naming)
const noexcept override;
std::string id() const noexcept override;
bool IsMember(const std::string& id) const noexcept override;
std::unique_ptr<Type> MemberType(
const std::string& id) const noexcept override;
std::size_t OffsetOf(const std::string& id) const override;
std::size_t OffsetOf(std::size_t index) const override;
std::size_t SlotCount() const noexcept override;

bool IsStruct() const noexcept override {
return true;
Expand All @@ -230,11 +243,13 @@ class UnionType : public RecordType {
UnionType(std::string id, std::vector<std::unique_ptr<Field>> fields)
: id_{std::move(id)}, fields_{std::move(fields)} {}

std::string id() // NOLINT(readability-identifier-naming)
const noexcept override;
std::string id() const noexcept override;
bool IsMember(const std::string& id) const noexcept override;
std::unique_ptr<Type> MemberType(
const std::string& id) const noexcept override;
std::size_t OffsetOf(const std::string& id) const override;
std::size_t OffsetOf(std::size_t index) const override;
std::size_t SlotCount() const noexcept override;

bool IsUnion() const noexcept override {
return true;
Expand Down
55 changes: 51 additions & 4 deletions src/qbe_ir_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,42 @@ void QbeIrGenerator::Visit(const ArrDeclNode& arr_decl) {
}
}

void QbeIrGenerator::Visit(const RecordDeclNode& struct_def) {}
void QbeIrGenerator::Visit(const RecordDeclNode& record_decl) {
/* Do nothing because this node only declares a type. */
}

void QbeIrGenerator::Visit(const FieldNode& field) {
/* Do nothing because this node only declares a member type in a record. */
}

void QbeIrGenerator::Visit(const FieldNode& field) {}
void QbeIrGenerator::Visit(const RecordVarDeclNode& record_var_decl) {
const auto base_addr = NextLocalNum();
// TODO: support different data types. We have `int` type for now.
WriteInstr_("{} =l alloc4 {}", FuncScopeTemp{base_addr},
record_var_decl.type->size());
id_to_num[record_var_decl.id] = base_addr;

void QbeIrGenerator::Visit(const RecordVarDeclNode& struct_def) {}
auto* record_type = dynamic_cast<RecordType*>(record_var_decl.type.get());
assert(record_type);
// NOTE: This predicate will make sure that we don't initialize members that
// exceed the total number of members in a record. Also, it gurantees
// that accessing element in the initializers will not go out of bound.
for (auto i = std::size_t{0}, e = record_var_decl.inits.size(),
slot_count = record_type->SlotCount();
i < slot_count && i < e; ++i) {
const auto& init = record_var_decl.inits.at(i);
init->Accept(*this);
const auto init_num = num_recorder.NumOfPrevExpr();

// res_addr = base_addr + offset
const int res_addr_num = NextLocalNum();
const auto offset = record_type->OffsetOf(i);
WriteInstr_("{} =l add {}, {}", FuncScopeTemp{res_addr_num},
FuncScopeTemp{base_addr}, offset);
WriteInstr_("storew {}, {}", FuncScopeTemp{init_num},
FuncScopeTemp{res_addr_num});
}
}

void QbeIrGenerator::Visit(const ParamNode& parameter) {
int id_num = NextLocalNum();
Expand Down Expand Up @@ -765,7 +796,23 @@ void QbeIrGenerator::Visit(const PostfixArithExprNode& postfix_expr) {
FuncScopeTemp{id_to_num.at(id_expr->id)});
}

void QbeIrGenerator::Visit(const RecordMemExprNode& mem_expr) {}
void QbeIrGenerator::Visit(const RecordMemExprNode& mem_expr) {
mem_expr.expr->Accept(*this);
const auto num = num_recorder.NumOfPrevExpr();
const auto id_num = reg_num_to_id_num.at(num);
auto* record_type = dynamic_cast<RecordType*>(mem_expr.expr->type.get());
assert(record_type);

const auto res_addr_num = NextLocalNum();
WriteInstr_("{} =l add {}, {}", FuncScopeTemp{res_addr_num},
FuncScopeTemp{id_num}, record_type->OffsetOf(mem_expr.id));

const int res_num = NextLocalNum();
WriteInstr_("{} =w loadw {}", FuncScopeTemp{res_num},
FuncScopeTemp{res_addr_num});
reg_num_to_id_num[res_num] = res_addr_num;
num_recorder.Record(res_num);
}

void QbeIrGenerator::Visit(const UnaryExprNode& unary_expr) {
unary_expr.operand->Accept(*this);
Expand Down
43 changes: 43 additions & 0 deletions src/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <algorithm>
#include <cstddef>
#include <memory>
#include <numeric>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -179,6 +181,35 @@ std::unique_ptr<Type> StructType::MemberType(
return std::make_unique<PrimType>(PrimitiveType::kUnknown);
}

std::size_t StructType::OffsetOf(const std::string& id) const {
std::size_t offset = 0;
for (auto i = std::size_t{0}, e = fields_.size(); i < e; ++i) {
const auto& field = fields_.at(i);
if (field->id == id) {
return offset;
}

offset += field->type->size();
}

throw std::runtime_error{"member not found in struct!"};
}

std::size_t StructType::OffsetOf(const std::size_t index) const {
if (index >= fields_.size()) {
throw std::out_of_range{"index out of bound!"};
}

auto end = std::next(fields_.cbegin(), (long)index);
return std::accumulate(
fields_.cbegin(), end, std::size_t{0},
[](auto&& size, auto&& field) { return size + field->type->size(); });
}

std::size_t StructType::SlotCount() const noexcept {
return fields_.size();
}

bool StructType::IsEqual(const Type& that) const noexcept {
if (const auto* that_struct = dynamic_cast<const StructType*>(&that)) {
if (that_struct->size() != size()) {
Expand Down Expand Up @@ -245,6 +276,18 @@ std::unique_ptr<Type> UnionType::MemberType(
return std::make_unique<PrimType>(PrimitiveType::kUnknown);
}

std::size_t UnionType::OffsetOf(const std::string& id) const {
return 0;
}

std::size_t UnionType::OffsetOf(const std::size_t index) const {
return 0;
}

std::size_t UnionType::SlotCount() const noexcept {
return fields_.size() > 0 ? 1 : 0;
}

bool UnionType::IsEqual(const Type& that) const noexcept {
if (const auto* that_union = dynamic_cast<const UnionType*>(&that)) {
return that_union->size() == size();
Expand Down
2 changes: 2 additions & 0 deletions src/type_checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ void TypeChecker::Visit(RecordVarDeclNode& record_var_decl) {
}
// TODO: May be file scope once we support global variables.
env_.AddSymbol(std::move(symbol), ScopeKind::kBlock);

record_var_decl.type = record_type->type->Clone();
}
}

Expand Down
22 changes: 22 additions & 0 deletions test/codegen/struct.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
int main() {
struct ss;

struct birth {
int date;
int month;
int year;
};

// Compiler will stop initializing elements at index 4 since we only have
// 3 members defined in `struct birth`.
struct birth bd1 = {3, 5, 1998, 2000, 2002};

__builtin_print(bd1.date);
__builtin_print(bd1.month);
__builtin_print(bd1.year);

bd1.date = bd1.date + 2;
__builtin_print(bd1.date);

return 0;
}
4 changes: 4 additions & 0 deletions test/codegen/struct.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
3
5
1998
5
25 changes: 25 additions & 0 deletions test/codegen/union.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
int main() {
union u;

union shape {
int square;
int circle;
int triangle;
};

// It's a legal case, but compilers like GCC may show warning of excessing elements.
// The value of the members is 3.
union shape s = {3, 4, 5};

// Every member shares the same starting memory address, so their value are the same.
__builtin_print(s.square);
__builtin_print(s.circle);
__builtin_print(s.triangle);

s.circle = 4;
__builtin_print(s.square);
__builtin_print(s.circle);
__builtin_print(s.triangle);

return 0;
}
6 changes: 6 additions & 0 deletions test/codegen/union.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
3
3
3
4
4
4
Loading