Merge branch 'exp2' into ref2

main
Su Xing 3 years ago
commit c9d506e68d

1
.gitignore vendored

@ -33,3 +33,4 @@
.cache
.~
doxygen

@ -52,3 +52,25 @@ cat /test/funcrparams.sysy
- 参照SysY语言规范修改`src/SysY.g4`文件实现SysY词法/语法的完整定义
- 修改任意代码后需要重新执行`cmake --build build`命令重新构建项目ANTLR工具会从`SysY.g4`生成词法/语法分析器,生成的文件位于`./build/src`目录
- (进阶内容)修改`src/ASTPrinter.h`与`src/ASTPrinter.cpp`实现从AST输出源程序但输出的源程序是经过格式化的测试用例为`test/format-test.sy`,格式化后的参考结果为`test/format-ref.sy`
## 实验2从AST生成中间表示
exp2分支为大家准备好了进行实验2的基本代码框架包括
- IR相关数据结构的定义`src/IR.h`
- 创建IR对象的工具类`src/IRBuilder.h`
- IR生成器的示例代码`src/SysYIRGenerator.h`
在实验2中同学们需要完成的任务包括
- 熟悉掌握IR定义与相关数据结构
- 从AST生成IR基于visitor机制
请同学们仔细阅读代码学习IR的定义。可以使用doxygen工具自动生成HTML文档
```bash
sudo apt install doxygen graphviz
doxygen doc/Doxyfile
```
上述命令执行完毕后将在doxygen/html下找到生成的代码文档。

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -5,7 +5,6 @@ antlr_target(SysYGen SysY.g4
LEXER PARSER
OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VISITOR
LISTENER
)
add_library(SysYParser SHARED ${ANTLR_SysYGen_CXX_OUTPUTS})
@ -14,8 +13,8 @@ target_link_libraries(SysYParser PUBLIC antlr4_shared)
add_executable(sysyc
sysyc.cpp
SysYFormatter.cpp
IR.cpp
SysYIRGenerator.cpp
)
target_include_directories(sysyc PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(sysyc PRIVATE SysYParser)

@ -45,6 +45,21 @@ Type *Type::getFunctionType(Type *returnType,
return FunctionType::get(returnType, paramTypes);
}
int Type::getSize() const {
switch (kind) {
case kInt:
case kFloat:
return 4;
case kLabel:
case kPointer:
case kFunction:
return 8;
case kVoid:
return 0;
}
return 0;
}
PointerType *PointerType::get(Type *baseType) {
static std::map<Type *, std::unique_ptr<PointerType>> pointerTypes;
auto iter = pointerTypes.find(baseType);
@ -59,16 +74,15 @@ PointerType *PointerType::get(Type *baseType) {
FunctionType *FunctionType::get(Type *returnType,
const std::vector<Type *> &paramTypes) {
static std::set<std::unique_ptr<FunctionType>> functionTypes;
auto iter = std::find_if(functionTypes.begin(), functionTypes.end(),
[&](const std::unique_ptr<FunctionType> &type) -> bool {
if (returnType != type->getReturnType() or
paramTypes.size() != type->getParamTypes().size())
return false;
for (int i = 0; i < paramTypes.size(); ++i)
if (paramTypes[i] != type->getParamTypes()[i])
return false;
return true;
});
auto iter =
std::find_if(functionTypes.begin(), functionTypes.end(),
[&](const std::unique_ptr<FunctionType> &type) -> bool {
if (returnType != type->getReturnType() or
paramTypes.size() != type->getParamTypes().size())
return false;
return std::equal(paramTypes.begin(), paramTypes.end(),
type->getParamTypes().begin());
});
if (iter != functionTypes.end())
return iter->get();
auto type = new FunctionType(returnType, paramTypes);
@ -83,7 +97,7 @@ void Value::replaceAllUsesWith(Value *value) {
uses.clear();
}
ConstantValue *ConstantValue::getInt(int value, const std::string &name) {
ConstantValue *ConstantValue::get(int value, const std::string &name) {
static std::map<int, std::unique_ptr<ConstantValue>> intConstants;
auto iter = intConstants.find(value);
if (iter != intConstants.end())
@ -94,7 +108,7 @@ ConstantValue *ConstantValue::getInt(int value, const std::string &name) {
return result.first->second.get();
}
ConstantValue *ConstantValue::getFloat(float value, const std::string &name) {
ConstantValue *ConstantValue::get(float value, const std::string &name) {
static std::map<float, std::unique_ptr<ConstantValue>> floatConstants;
auto iter = floatConstants.find(value);
if (iter != floatConstants.end())
@ -117,4 +131,16 @@ void User::replaceOperand(int index, Value *value) {
use.setValue(value);
}
CallInst::CallInst(Function *callee, const std::vector<Value *> args,
BasicBlock *parent, const std::string &name)
: Instruction(kCall, callee->getReturnType(), parent, name) {
addOperand(callee);
for (auto arg : args)
addOperand(arg);
}
Function *CallInst::getCallee() {
return dynamic_cast<Function *>(getOperand(0));
}
} // namespace sysy

@ -1,47 +1,37 @@
#pragma once
#include "range.h"
#include <cassert>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
namespace sysy {
template <typename IterT> struct range {
using iterator = IterT;
using value_type = typename std::iterator_traits<iterator>::value_type;
using reference = typename std::iterator_traits<iterator>::reference;
iterator b;
iterator e;
explicit range(iterator b, iterator e) : b(b), e(e) {}
iterator begin() { return b; }
iterator end() { return e; }
};
template <typename IterT> range<IterT> make_range(IterT b, IterT e) {
return range(b, e);
}
//===----------------------------------------------------------------------===//
// Types
//
// The SysY type system is quite simple.
// 1. The base class `Type` is used to represent all primitive scalar types,
// include `int`, `float`, `void`, and the label type representing branch
// targets.
// 2. `PointerType` and `FunctionType` derive from `Type` and represent pointer
// type and function type, respectively.
//
// NOTE `Type` and its derived classes have their ctors declared as 'protected'.
// Users must use Type::getXXXType() methods to obtain `Type` pointers.
//===----------------------------------------------------------------------===//
/*!
* \defgroup type Types
* The SysY type system is quite simple.
* 1. The base class `Type` is used to represent all primitive scalar types,
* include `int`, `float`, `void`, and the label type representing branch
* targets.
* 2. `PointerType` and `FunctionType` derive from `Type` and represent pointer
* type and function type, respectively.
*
* NOTE `Type` and its derived classes have their ctors declared as 'protected'.
* Users must use Type::getXXXType() methods to obtain `Type` pointers.
* @{
*/
/*!
* `Type` is used to represent all primitive scalar types,
* include `int`, `float`, `void`, and the label type representing branch
* targets
*/
class Type {
public:
enum Kind {
@ -66,7 +56,6 @@ public:
static Type *getPointerType(Type *baseType);
static Type *getFunctionType(Type *returnType,
const std::vector<Type *> &paramTypes = {});
static int getTypeSize();
public:
Kind getKind() const { return kind; }
@ -76,8 +65,14 @@ public:
bool isLabel() const { return kind == kLabel; }
bool isPointer() const { return kind == kPointer; }
bool isFunction() const { return kind == kFunction; }
int getSize() const;
template <typename T>
std::enable_if_t<std::is_base_of_v<Type, T>, T *> as() const {
return dynamic_cast<T *>(const_cast<Type *>(this));
}
}; // class Type
//! Pointer type
class PointerType : public Type {
protected:
Type *baseType;
@ -92,6 +87,7 @@ public:
Type *getBaseType() const { return baseType; }
}; // class PointerType
//! Function type
class FunctionType : public Type {
private:
Type *returnType;
@ -107,49 +103,73 @@ public:
public:
Type *getReturnType() const { return returnType; }
const std::vector<Type *> &getParamTypes() const { return paramTypes; }
auto getParamTypes() const { return make_range(paramTypes); }
int getNumParams() const { return paramTypes.size(); }
}; // class FunctionType
//===----------------------------------------------------------------------===//
// Values
//
// description
//===----------------------------------------------------------------------===//
/*!
* @}
*/
/*!
* \defgroup ir IR
*
* The SysY IR is an instruction level language. The IR is orgnized
* as a four-level tree structure, as shown below
*
* \dotfile ir-4level.dot IR Structure
*
* - `Module` corresponds to the top level "CompUnit" syntax structure
* - `GlobalValue` corresponds to the "Decl" syntax structure
* - `Function` corresponds to the "FuncDef" syntax structure
* - `BasicBlock` is a sequence of instructions without branching. A `Function`
* made up by one or more `BasicBlock`s.
* - `Instruction` represents a primitive operation on values, e.g., add or sub.
*
* The fundamental data concept in SysY IR is `Value`. A `Value` is like
* a register and is used by `Instruction`s as input/output operand. Each value
* has an associated `Type` indicating the data type held by the value.
*
* Most `Instruction`s have a three-address signature, i.e., there are at most 2
* input values and at most 1 output value.
*
* The SysY IR adots a Static-Single-Assignment (SSA) design. That is, `Value`
* is defined (as the output operand ) by some instruction, and used (as the
* input operand) by other instructions. While a value can be used by multiple
* instructions, the `definition` occurs only once. As a result, there is a
* one-to-one relation between a value and the instruction defining it. In other
* words, any instruction defines a value can be viewed as the defined value
* itself. So `Instruction` is also a `Value` in SysY IR. See `Value` for the
* type hierachy.
*
* @{
*/
class User;
class Value;
//! `Use` represents the relation between a `Value` and its `User`
class Use {
public:
enum Kind {
kRead,
kWrite,
kReadWrite,
};
private:
Kind kind;
//! the position of value in the user's operands, i.e.,
//! user->getOperands[index] == value
int index;
User *user;
Value *value;
public:
Use() = default;
Use(Kind kind, int index, User *user, Value *value)
: kind(kind), index(index), user(user), value(value) {}
Use(int index, User *user, Value *value)
: index(index), user(user), value(value) {}
public:
Kind getKind() const { return kind; }
int getIndex() const { return index; }
User *getUser() const { return user; }
Value *getValue() const { return value; }
bool isRead() const { return kind == kRead; }
bool isWrite() const { return kind == kWrite; }
bool isReadWrite() const { return kind == kReadWrite; }
void setValue(Value *value) { value = value; }
}; // class Use
//! The base class of all value types
class Value {
protected:
Type *type;
@ -172,43 +192,57 @@ public:
void removeUse(Use *use) { uses.remove(use); }
}; // class Value
/*!
* Static constants known at compile time.
*
* `ConstantValue`s are not defined by instructions, and do not use any other
* `Value`s. It's type is either `int` or `float`.
*/
class ConstantValue : public Value {
friend class IRBuilder;
protected:
union {
int iConstant;
float fConstant;
int iScalar;
float fScalar;
};
protected:
ConstantValue(int value, const std::string &name = "")
: Value(Type::getIntType(), name), iConstant(value) {}
: Value(Type::getIntType(), name), iScalar(value) {}
ConstantValue(float value, const std::string &name = "")
: Value(Type::getFloatType(), name), fConstant(value) {}
: Value(Type::getFloatType(), name), fScalar(value) {}
public:
static ConstantValue *getInt(int value, const std::string &name = "");
static ConstantValue *getFloat(float value, const std::string &name = "");
static ConstantValue *get(int value, const std::string &name = "");
static ConstantValue *get(float value, const std::string &name = "");
public:
int getInt() const {
assert(isInt());
return iConstant;
return iScalar;
}
float getFloat() const {
assert(isFloat());
return fConstant;
return fScalar;
}
}; // class ConstantValue
class BasicBlock;
/*!
* Arguments of `BasicBlock`s.
*
* SysY IR is an SSA language, however, it does not use PHI instructions as in
* LLVM IR. `Value`s from different predecessor blocks are passed explicitly as
* block arguments. This is also the approach used by MLIR.
* NOTE that `Function` does not own `Argument`s, function arguments are
* implemented as its entry block's arguments.
*/
class Argument : public Value {
protected:
BasicBlock *block;
int index;
protected:
public:
Argument(Type *type, BasicBlock *block, int index,
const std::string &name = "")
: Value(type, name), block(block), index(index) {}
@ -216,6 +250,13 @@ protected:
class Instruction;
class Function;
/*!
* The container for `Instruction` sequence.
*
* `BasicBlock` maintains a list of `Instruction`s, with the last one being
* a terminator (branch or return). Besides, `BasicBlock` stores its arguments
* and records its predecessor and successor `BasicBlock`s.
*/
class BasicBlock : public Value {
friend class Function;
@ -238,6 +279,7 @@ protected:
arguments(), successors(), predecessors() {}
public:
int getNumInstructions() const { return instructions.size(); }
int getNumArguments() const { return arguments.size(); }
int getNumPredecessors() const { return predecessors.size(); }
int getNumSuccessors() const { return successors.size(); }
@ -249,8 +291,15 @@ public:
iterator begin() { return instructions.begin(); }
iterator end() { return instructions.end(); }
iterator terminator() { return std::prev(end()); }
Argument *createArgument(Type *type, const std::string &name = "") {
arguments.emplace_back(type, this, arguments.size(), name);
return &arguments.back();
};
}; // class BasicBlock
//! User is the abstract base type of `Value` types which use other `Value` as
//! operands. Currently, there are two kinds of `User`s, `Instruction` and
//! `GlobalValue`.
class User : public Value {
protected:
std::vector<Use> operands;
@ -259,15 +308,27 @@ protected:
User(Type *type, const std::string &name = "")
: Value(type, name), operands() {}
public:
struct operand_iterator : std::vector<Use>::iterator {
using Base = std::vector<Use>::iterator;
using Base::Base;
using value_type = Value *;
value_type operator->() { return operator*().getValue(); }
};
public:
int getNumOperands() const { return operands.size(); }
const std::vector<Use> &getOperands() const { return operands; }
const Use &getOperand(int index) const { return operands[index]; }
void addOperand(Value *value, Use::Kind mode = Use::kRead) {
operands.emplace_back(mode, operands.size(), this, value);
auto operand_begin() const { return operands.begin(); }
auto operand_end() const { return operands.end(); }
auto getOperands() const {
return make_range(operand_begin(), operand_end());
}
Value *getOperand(int index) const { return operands[index].getValue(); }
void addOperand(Value *value) {
operands.emplace_back(operands.size(), this, value);
value->addUse(&operands.back());
}
void addOperands(const std::vector<Value *> &operands) {
template <typename ContainerT> void addOperands(const ContainerT &operands) {
for (auto value : operands)
addOperand(value);
}
@ -276,6 +337,9 @@ public:
const std::string &getName() const { return name; }
}; // class User
/*!
* Base of all concrete instruction types.
*/
class Instruction : public User {
public:
enum Kind : uint64_t {
@ -337,24 +401,47 @@ public:
BasicBlock *getParent() const { return parent; }
void setParent(BasicBlock *bb) { parent = bb; }
bool isCmp() const {
static constexpr uint64_t CondOpMask =
bool isBinary() const {
static constexpr uint64_t BinaryOpMask =
(kAdd | kSub | kMul | kDiv | kRem) |
(kICmpEQ | kICmpNE | kICmpLT | kICmpGT | kICmpLE | kICmpGE) |
(kFAdd | kFSub | kFMul | kFDiv | kFRem) |
(kFCmpEQ | kFCmpNE | kFCmpLT | kFCmpGT | kFCmpLE | kFCmpGE);
return kind & CondOpMask;
return kind & BinaryOpMask;
}
bool isUnary() const {
static constexpr uint64_t UnaryOpMask = kNeg | kNot | kFNeg | kFtoI | kIToF;
return kind & UnaryOpMask;
}
bool isMemory() const {
static constexpr uint64_t MemoryOpMask = kAlloca | kLoad | kStore;
return kind & MemoryOpMask;
}
bool isTerminator() {
bool isTerminator() const {
static constexpr uint64_t TerminatorOpMask = kCondBr | kBr | kReturn;
return kind & TerminatorOpMask;
}
bool isCommutative() {
bool isCmp() const {
static constexpr uint64_t CmpOpMask =
(kICmpEQ | kICmpNE | kICmpLT | kICmpGT | kICmpLE | kICmpGE) |
(kFCmpEQ | kFCmpNE | kFCmpLT | kFCmpGT | kFCmpLE | kFCmpGE);
return kind & CmpOpMask;
}
bool isBranch() const {
static constexpr uint64_t BranchOpMask = kBr | kCondBr;
return kind & BranchOpMask;
}
bool isCommutative() const {
static constexpr uint64_t CommutativeOpMask =
kAdd | kMul | kICmpEQ | kICmpNE | kFAdd | kFMul | kFCmpEQ | kFCmpNE;
return kind & CommutativeOpMask;
}
bool isUnconditional() const { return kind == kBr; }
bool isConditional() const { return kind == kCondBr; }
}; // class Instruction
class Function;
//! Function call.
class CallInst : public Instruction {
friend class IRBuilder;
@ -365,10 +452,11 @@ protected:
public:
Function *getCallee();
auto getArguments() {
return make_range(std::next(operands.begin()), operands.end());
return make_range(std::next(operand_begin()), operand_end());
}
}; // class CallInst
//! Unary instruction, includes '!', '-' and type conversion.
class UnaryInst : public Instruction {
friend class IRBuilder;
@ -380,9 +468,10 @@ protected:
}
public:
Value *getOperand() const { return operands[0].getValue(); }
Value *getOperand() const { return User::getOperand(0); }
}; // class UnaryInst
//! Binary instruction, e.g., arithmatic, relation, logic, etc.
class BinaryInst : public Instruction {
friend class IRBuilder;
@ -395,42 +484,36 @@ protected:
}
public:
Value *getLhs() const { return operands[0].getValue(); }
Value *getRhs() const { return operands[1].getValue(); }
Value *getLhs() const { return getOperand(0); }
Value *getRhs() const { return getOperand(1); }
}; // class BinaryInst
class TerminatorInst : public Instruction {
protected:
using Instruction::Instruction;
}; // TerminatorInst
class ReturnInst : public TerminatorInst {
//! The return statement
class ReturnInst : public Instruction {
friend class IRBuilder;
protected:
ReturnInst(Value *value = nullptr, BasicBlock *parent = nullptr)
: TerminatorInst(kReturn, Type::getVoidType(), parent, "") {
: Instruction(kReturn, Type::getVoidType(), parent, "") {
if (value)
addOperand(value);
}
}; // class ReturnInst
class BranchInst : public TerminatorInst {
protected:
using TerminatorInst::TerminatorInst;
public:
bool isUnconditional() const { return kind == kBr; }
bool isConditional() const { return kind == kCondBr; }
}; // class BranchInst
bool hasReturnValue() const { return not operands.empty(); }
Value *getReturnValue() const {
return hasReturnValue() ? getOperand(0) : nullptr;
}
}; // class ReturnInst
class UncondBrInst : public BranchInst {
//! Unconditional branch
class UncondBrInst : public Instruction {
friend class IRBuilder;
protected:
UncondBrInst(BasicBlock *block, std::vector<Value *> args,
BasicBlock *parent = nullptr)
: BranchInst(kCondBr, Type::getVoidType(), parent, "") {
: Instruction(kCondBr, Type::getVoidType(), parent, "") {
assert(block->getNumArguments() == args.size());
addOperand(block);
addOperands(args);
@ -438,21 +521,22 @@ protected:
public:
BasicBlock *getBlock() const {
return dynamic_cast<BasicBlock *>(getOperand(0).getValue());
return dynamic_cast<BasicBlock *>(getOperand(0));
}
auto getArguments() const {
return make_range(std::next(operands.begin()), operands.end());
}
}; // class UncondBrInst
class CondBrInst : public BranchInst {
//! Conditional branch
class CondBrInst : public Instruction {
friend class IRBuilder;
protected:
CondBrInst(Value *condition, BasicBlock *thenBlock, BasicBlock *elseBlock,
const std::vector<Value *> &thenArgs,
const std::vector<Value *> &elseArgs, BasicBlock *parent = nullptr)
: BranchInst(kCondBr, Type::getVoidType(), parent, "") {
: Instruction(kCondBr, Type::getVoidType(), parent, "") {
assert(thenBlock->getNumArguments() == thenArgs.size() and
elseBlock->getNumArguments() == elseArgs.size());
addOperand(condition);
@ -464,10 +548,10 @@ protected:
public:
BasicBlock *getThenBlock() const {
return dynamic_cast<BasicBlock *>(getOperand(0).getValue());
return dynamic_cast<BasicBlock *>(getOperand(0));
}
BasicBlock *getElseBlock() const {
return dynamic_cast<BasicBlock *>(getOperand(1).getValue());
return dynamic_cast<BasicBlock *>(getOperand(1));
}
auto getThenArguments() const {
auto begin = operands.begin() + 2;
@ -481,57 +565,53 @@ public:
}
}; // class CondBrInst
class MemoryInst : public Instruction {
protected:
using Instruction::Instruction;
}; // class MemoryInst
class AllocaInst : public MemoryInst {
//! Allocate memory for stack variables, used for non-global variable declartion
class AllocaInst : public Instruction {
friend class IRBuilder;
protected:
AllocaInst(Type *type, const std::vector<Value *> &dims = {},
BasicBlock *parent = nullptr, const std::string &name = "")
: MemoryInst(kAlloca, type, parent, name) {
: Instruction(kAlloca, type, parent, name) {
addOperands(dims);
}
public:
int getNumDims() const { return getNumOperands(); }
auto &getDims() const { return operands; }
Value *getDim(int index) { return getOperand(index).getValue(); }
auto getDims() const { return getOperands(); }
Value *getDim(int index) { return getOperand(index); }
}; // class AllocaInst
class LoadInst : public MemoryInst {
//! Load a value from memory address specified by a pointer value
class LoadInst : public Instruction {
friend class IRBuilder;
protected:
LoadInst(Value *pointer, const std::vector<Value *> &indices = {},
BasicBlock *parent = nullptr, const std::string &name = "")
: MemoryInst(
kLoad,
dynamic_cast<PointerType *>(pointer->getType())->getBaseType(),
parent, name) {
: Instruction(kLoad, pointer->getType()->as<PointerType>()->getBaseType(),
parent, name) {
addOperands(indices);
}
public:
int getNumIndices() const { return getNumOperands() - 1; }
Value *getPointer() const { return operands.front().getValue(); }
Value *getPointer() const { return getOperand(0); }
auto getIndices() const {
return make_range(std::next(operands.begin()), operands.end());
return make_range(std::next(operand_begin()), operand_end());
}
Value *getIndex(int index) const { return getOperand(index + 1).getValue(); }
Value *getIndex(int index) const { return getOperand(index + 1); }
}; // class LoadInst
class StoreInst : public MemoryInst {
//! Store a value to memory address specified by a pointer value
class StoreInst : public Instruction {
friend class IRBuilder;
protected:
StoreInst(Value *value, Value *pointer,
const std::vector<Value *> &indices = {},
BasicBlock *parent = nullptr, const std::string &name = "")
: MemoryInst(kStore, Type::getVoidType(), parent, name) {
: Instruction(kStore, Type::getVoidType(), parent, name) {
addOperand(value);
addOperand(pointer);
addOperands(indices);
@ -539,15 +619,16 @@ protected:
public:
int getNumIndices() const { return getNumOperands() - 2; }
Value *getValue() const { return operands[0].getValue(); }
Value *getPointer() const { return operands[1].getValue(); }
Value *getValue() const { return getOperand(0); }
Value *getPointer() const { return getOperand(1); }
auto getIndices() const {
return make_range(operands.begin() + 2, operands.end());
return make_range(operand_begin() + 2, operand_end());
}
Value *getIndex(int index) const { return getOperand(index + 2).getValue(); }
Value *getIndex(int index) const { return getOperand(index + 2); }
}; // class StoreInst
class Module;
//! Function definition
class Function : public Value {
friend class Module;
@ -566,12 +647,12 @@ protected:
public:
Type *getReturnType() const {
return dynamic_cast<FunctionType *>(getType())->getReturnType();
return getType()->as<FunctionType>()->getReturnType();
}
auto &getParamTypes() const {
return dynamic_cast<FunctionType *>(getType())->getParamTypes();
auto getParamTypes() const {
return getType()->as<FunctionType>()->getParamTypes();
}
block_list &getBasicBlocks() { return blocks; }
auto getBasicBlocks() { return make_range(blocks); }
BasicBlock *getEntryBlock() { return blocks.front().get(); }
BasicBlock *addBasicBlock(const std::string &name = "") {
blocks.emplace_back(new BasicBlock(this, name));
@ -584,24 +665,31 @@ public:
}
}; // class Function
//! Global value declared at file scope
class GlobalValue : public User {
friend class Module;
protected:
Module *parent;
bool hasInit;
protected:
GlobalValue(Module *parent, Type *type, const std::string &name,
const std::vector<Value *> &dims = {})
: User(type, name), parent(parent) {
const std::vector<Value *> &dims = {}, Value *init = nullptr)
: User(type, name), parent(parent), hasInit(init) {
assert(type->isPointer());
addOperands(dims);
if (init)
addOperand(init);
}
public:
int getNumDims() const { return getNumOperands(); }
Value *getDim(int index) { return getOperand(index).getValue(); }
Value *init() const { return hasInit ? operands.back().getValue() : nullptr; }
int getNumDims() const { return getNumOperands() - (hasInit ? 1 : 0); }
Value *getDim(int index) { return getOperand(index); }
}; // class GlobalValue
//! IR unit for representing a SysY compile unit
class Module {
protected:
std::map<std::string, std::unique_ptr<Function>> functions;
@ -639,16 +727,7 @@ public:
}
}; // class Module
inline CallInst::CallInst(Function *callee, const std::vector<Value *> args,
BasicBlock *parent, const std::string &name)
: Instruction(kCall, callee->getReturnType(), parent, name) {
addOperand(callee);
for (auto arg : args)
addOperand(arg);
}
inline Function *CallInst::getCallee() {
return dynamic_cast<Function *>(getOperand(0).getValue());
}
/*!
* @}
*/
} // namespace sysy

@ -1,28 +0,0 @@
#include <algorithm>
#include <iostream>
using namespace std;
#include "SysYParser.h"
#include "SysYFormatter.h"
// any ASTPrinter::visitNumber(SysYParser::NumberContext *ctx) {
// cout << ctx->IntConst()->getText();
// return nullptr;
// }
// any ASTPrinter::visitString(SysYParser::StringContext *ctx) {
// cout << ctx->String()->getText();
// return nullptr;
// }
// any ASTPrinter::visitFuncRParams(SysYParser::FuncRParamsContext *ctx) {
// if (ctx->funcRParam().empty())
// return nullptr;
// auto numParams = ctx->funcRParam().size();
// ctx->funcRParam(0)->accept(this);
// for (int i = 1; i < numParams; ++i) {
// cout << ", ";
// ctx->funcRParam(i)->accept(this);
// }
// cout << '\n';
// return nullptr;
// }

@ -4,6 +4,8 @@
#include "SysYParser.h"
#include <ostream>
namespace sysy {
class SysYFormatter : public SysYBaseVisitor {
protected:
std::ostream &os;
@ -331,4 +333,6 @@ public:
interleave(ctx->exp(), ", ");
return 0;
}
};
};
} // namespace sysy

@ -10,26 +10,56 @@ any SysYIRGenerator::visitModule(SysYParser::ModuleContext *ctx) {
auto pModule = new Module();
assert(pModule);
module.reset(pModule);
// turn on global flag
isLocal = true;
return visitChildren(ctx);
visitChildren(ctx);
return pModule;
}
any SysYIRGenerator::visitGlobalDecl(SysYParser::DeclContext *ctx) {
bool isConst = ctx->CONST();
Type *type = ctx->btype()->INT() ? Type::getIntType() : Type::getFloatType();
any SysYIRGenerator::visitFunc(SysYParser::FuncContext *ctx) {
auto name = ctx->ID()->getText();
auto params = ctx->funcFParams()->funcFParam();
vector<Type *> paramTypes;
vector<string> paramNames;
for (auto param : params) {
paramTypes.push_back(any_cast<Type *>(visitBtype(param->btype())));
paramNames.push_back(param->ID()->getText());
}
Type *returnType = any_cast<Type *>(visitFuncType(ctx->funcType()));
auto funcType = Type::getFunctionType(returnType, paramTypes);
auto function = module->createFunction(name, funcType);
auto entry = function->getEntryBlock();
for (auto i = 0; i < paramTypes.size(); ++i)
entry->createArgument(paramTypes[i], paramNames[i]);
builder.setPosition(entry, entry->end());
visitBlockStmt(ctx->blockStmt());
return function;
}
any SysYIRGenerator::visitBtype(SysYParser::BtypeContext *ctx) {
return ctx->INT() ? Type::getIntType() : Type::getFloatType();
}
any SysYIRGenerator::visitBlockStmt(SysYParser::BlockStmtContext *ctx) {
for (auto item : ctx->blockItem())
visitBlockItem(item);
return builder.getBasicBlock();
}
any SysYIRGenerator::visitBlockItem(SysYParser::BlockItemContext *ctx) {
return ctx->decl() ? visitDecl(ctx->decl()) : visitStmt(ctx->stmt());
}
any SysYIRGenerator::visitDecl(SysYParser::DeclContext *ctx) {
std::vector<Value *> values;
auto type = any_cast<Type *>(visitBtype(ctx->btype()));
for (auto varDef : ctx->varDef()) {
auto lValue = varDef->lValue();
auto id = lValue->ID()->getText();
vector<Value *> dims;
for (auto exp : lValue->exp()) {
auto res = exp->accept(this);
assert(res.has_value());
auto dim = any_cast<Value *>(res);
// dynamic_cast<C>(expression)
// dims.push_back();
auto name = varDef->lValue()->ID()->getText();
auto alloca = builder.createAllocaInst(type, {}, name);
if (varDef->ASSIGN()) {
auto value = any_cast<Value *>(varDef->initValue()->accept(this));
auto store = builder.createStoreInst(value, alloca);
}
values.push_back(alloca);
}
return visitChildren(ctx);
return values;
}
} // namespace sysy

@ -1,44 +1,28 @@
#pragma once
#include <memory>
#include "IR.h"
#include "IRBuilder.h"
#include "SysYBaseVisitor.h"
#include "SysYParser.h"
#include <memory>
namespace sysy {
class SysYIRGenerator : public SysYBaseVisitor {
private:
struct LocalMode {
bool &mode;
explicit LocalMode(bool &mode) : mode(mode) { mode = true; }
~LocalMode() { mode = false; }
};
private:
std::unique_ptr<Module> module;
bool isLocal;
IRBuilder builder;
public:
SysYIRGenerator() = default;
private:
std::any visitGlobalDecl(SysYParser::DeclContext *ctx);
std::any visitLocalDecl(SysYParser::DeclContext *ctx);
public:
virtual std::any visitModule(SysYParser::ModuleContext *ctx) override;
virtual std::any visitDecl(SysYParser::DeclContext *ctx) override {
return isLocal? visitLocalDecl(ctx) : visitGlobalDecl(ctx);
}
virtual std::any visitBtype(SysYParser::BtypeContext *ctx) override {
return visitChildren(ctx);
}
virtual std::any visitDecl(SysYParser::DeclContext *ctx) override;
virtual std::any visitBtype(SysYParser::BtypeContext *ctx) override;
virtual std::any visitVarDef(SysYParser::VarDefContext *ctx) override {
return visitChildren(ctx);
}
@ -53,10 +37,7 @@ public:
return visitChildren(ctx);
}
virtual std::any visitFunc(SysYParser::FuncContext *ctx) override {
LocalMode guard(isLocal);
return visitChildren(ctx);
}
virtual std::any visitFunc(SysYParser::FuncContext *ctx) override;
virtual std::any visitFuncType(SysYParser::FuncTypeContext *ctx) override {
return visitChildren(ctx);
@ -72,13 +53,9 @@ public:
return visitChildren(ctx);
}
virtual std::any visitBlockStmt(SysYParser::BlockStmtContext *ctx) override {
return visitChildren(ctx);
}
virtual std::any visitBlockStmt(SysYParser::BlockStmtContext *ctx) override;
virtual std::any visitBlockItem(SysYParser::BlockItemContext *ctx) override {
return visitChildren(ctx);
}
virtual std::any visitBlockItem(SysYParser::BlockItemContext *ctx) override;
virtual std::any visitStmt(SysYParser::StmtContext *ctx) override {
return visitChildren(ctx);

@ -0,0 +1,61 @@
#pragma once
#include <iterator>
namespace sysy {
/*!
* \defgroup utility Utilities
* @{
*/
/*!
* \brief `range` is an simple wrapper of an iterator pair [begin, end)
*
* Example usage
*
* ```cpp
* vector<int> v = {1,2,3};
* auto rg = make_range(v);
* for (auto v : rg)
* cout << v << '\n';
* ```
*/
template <typename IterT> struct range {
using iterator = IterT;
using value_type = typename std::iterator_traits<iterator>::value_type;
using reference = typename std::iterator_traits<iterator>::reference;
private:
iterator b;
iterator e;
public:
explicit range(iterator b, iterator e) : b(b), e(e) {}
iterator begin() { return b; }
iterator end() { return e; }
auto size() const { return std::distance(b, e); }
auto empty() const { return b == e; }
};
//! create `range` object from iterator pair [begin, end)
template <typename IterT> range<IterT> make_range(IterT b, IterT e) {
return range(b, e);
}
//! create `range` object from a container who has `begin()` and `end()` methods
template <typename ContainerT>
range<typename ContainerT::iterator> make_range(ContainerT &c) {
return make_range(c.begin(), c.end());
}
//! create `range` object from a container who has `begin()` and `end()` methods
template <typename ContainerT>
range<typename ContainerT::const_iterator> make_range(const ContainerT &c) {
return make_range(c.begin(), c.end());
}
/*!
* @}
*/
} // namespace sysy

@ -4,10 +4,12 @@
#include <fstream>
#include <iostream>
using namespace std;
using namespace antlr4;
#include "SysYFormatter.h"
#include "SysYLexer.h"
#include "SysYParser.h"
using namespace antlr4;
#include "SysYFormatter.h"
#include "SysYIRGenerator.h"
using namespace sysy;
int main(int argc, char **argv) {
if (argc != 2) {
@ -24,12 +26,9 @@ int main(int argc, char **argv) {
CommonTokenStream tokens(&lexer);
SysYParser parser(&tokens);
auto module = parser.module();
// tree::ParseTreeWalker walker;
// SysYASTPrinter printer(cout);
// cout << module->getText() << '\n';
// walker.walk(&printer, module);
SysYFormatter formatter(cout);
formatter.visitModule(module);
SysYIRGenerator generator;
generator.visitModule(module);
return EXIT_SUCCESS;
}
Loading…
Cancel
Save