Compare commits
49 Commits
master
...
feature/la
| Author | SHA1 | Date |
|---|---|---|
|
|
1c7cc33e4b | 1 week ago |
|
|
6f649c0ad5 | 1 week ago |
|
|
ec56841167 | 1 week ago |
|
|
c8f40ea09a | 2 weeks ago |
|
|
d9201de428 | 2 weeks ago |
|
|
700bbb4e3b | 2 weeks ago |
|
|
728de089ff | 2 weeks ago |
|
|
bc4400c1c7 | 2 weeks ago |
|
|
c945a61f90 | 2 weeks ago |
|
|
0826c86772 | 2 weeks ago |
|
|
ce25b612bf | 2 weeks ago |
|
|
fa76f0fbfc | 2 weeks ago |
|
|
6e129b3a56 | 2 weeks ago |
|
|
5e7e8a6ff2 | 2 weeks ago |
|
|
9c7095bba7 | 2 weeks ago |
|
|
97c01debbd | 2 weeks ago |
|
|
f53f7ec82c | 2 weeks ago |
|
|
affccfb27c | 2 weeks ago |
|
|
b93d769576 | 2 weeks ago |
|
|
9efcdde353 | 2 weeks ago |
|
|
74f325c6fd | 2 weeks ago |
|
|
8477ab4aca | 2 weeks ago |
|
|
8e42c77caf | 2 weeks ago |
|
|
bff9a5d296 | 2 weeks ago |
|
|
6cc1908515 | 2 weeks ago |
|
|
e0c8898dae | 2 weeks ago |
|
|
310c93feac | 2 weeks ago |
|
|
066db23e40 | 2 weeks ago |
|
|
40403e955d | 2 weeks ago |
|
|
7eed5fe6f6 | 2 weeks ago |
|
|
4098545062 | 2 weeks ago |
|
|
f6773274dd | 2 weeks ago |
|
|
e1a4f30488 | 2 weeks ago |
|
|
f3fe34801e | 2 weeks ago |
|
|
d175189193 | 2 weeks ago |
|
|
d1f2efa4ae | 2 weeks ago |
|
|
a015a4bc30 | 3 weeks ago |
|
|
c4479eaa1e | 3 weeks ago |
|
|
a0d5288351 | 3 weeks ago |
|
|
5d4bb511d1 | 3 weeks ago |
|
|
c450277c83 | 3 weeks ago |
|
|
c21c066e4d | 3 weeks ago |
|
|
2070193fcd | 3 weeks ago |
|
|
a1ba450950 | 3 weeks ago |
|
|
56753bc842 | 3 weeks ago |
|
|
43e257368f | 3 weeks ago |
|
|
4f8dec86d7 | 3 weeks ago |
|
|
3b3318613a | 3 weeks ago |
|
|
a8bd6d647d | 3 weeks ago |
@ -1,17 +1,187 @@
|
||||
// 极简符号表:记录局部变量定义点。
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "SysYParser.h"
|
||||
#include "ir/IR.h"
|
||||
|
||||
// 符号种类
|
||||
enum class SymbolKind {
|
||||
Variable,
|
||||
Function,
|
||||
Parameter,
|
||||
Constant
|
||||
};
|
||||
|
||||
// 符号条目
|
||||
// 符号条目
|
||||
struct Symbol {
|
||||
// 基本信息
|
||||
std::string name;
|
||||
SymbolKind kind;
|
||||
std::shared_ptr<ir::Type> type;
|
||||
int scope_level = 0;
|
||||
int stack_offset = -1;
|
||||
bool is_initialized = false;
|
||||
bool is_builtin = false;
|
||||
|
||||
// 数组参数相关
|
||||
std::vector<int> array_dims;
|
||||
bool is_array_param = false;
|
||||
|
||||
// 函数相关
|
||||
std::vector<std::shared_ptr<ir::Type>> param_types;
|
||||
|
||||
// 常量值存储
|
||||
union ConstantValue {
|
||||
int i32;
|
||||
float f32;
|
||||
};
|
||||
|
||||
// 标量常量
|
||||
bool is_int_const = true;
|
||||
ConstantValue const_value;
|
||||
|
||||
// 数组常量(扁平化存储)
|
||||
bool is_array_const = false;
|
||||
std::vector<ConstantValue> array_const_values;
|
||||
|
||||
// 语法树节点
|
||||
SysYParser::VarDefContext* var_def_ctx = nullptr;
|
||||
SysYParser::ConstDefContext* const_def_ctx = nullptr;
|
||||
SysYParser::FuncFParamContext* param_def_ctx = nullptr;
|
||||
SysYParser::FuncDefContext* func_def_ctx = nullptr;
|
||||
|
||||
// 辅助方法
|
||||
bool IsScalarConstant() const {
|
||||
return kind == SymbolKind::Constant && !type->IsArray();
|
||||
}
|
||||
|
||||
bool IsArrayConstant() const {
|
||||
return kind == SymbolKind::Constant && type->IsArray();
|
||||
}
|
||||
|
||||
int GetIntConstant() const {
|
||||
if (!IsScalarConstant()) {
|
||||
throw std::runtime_error("不是标量常量");
|
||||
}
|
||||
if (!is_int_const) {
|
||||
throw std::runtime_error("不是整型常量");
|
||||
}
|
||||
return const_value.i32;
|
||||
}
|
||||
|
||||
float GetFloatConstant() const {
|
||||
if (!IsScalarConstant()) {
|
||||
throw std::runtime_error("不是标量常量");
|
||||
}
|
||||
if (is_int_const) {
|
||||
return static_cast<float>(const_value.i32);
|
||||
}
|
||||
return const_value.f32;
|
||||
}
|
||||
|
||||
ConstantValue GetArrayElement(size_t index) const {
|
||||
if (!IsArrayConstant()) {
|
||||
throw std::runtime_error("不是数组常量");
|
||||
}
|
||||
if (index >= array_const_values.size()) {
|
||||
throw std::runtime_error("数组下标越界");
|
||||
}
|
||||
return array_const_values[index];
|
||||
}
|
||||
|
||||
size_t GetArraySize() const {
|
||||
if (!IsArrayConstant()) return 0;
|
||||
return array_const_values.size();
|
||||
}
|
||||
};
|
||||
class SymbolTable {
|
||||
public:
|
||||
void Add(const std::string& name, SysYParser::VarDefContext* decl);
|
||||
bool Contains(const std::string& name) const;
|
||||
SysYParser::VarDefContext* Lookup(const std::string& name) const;
|
||||
public:
|
||||
SymbolTable();
|
||||
~SymbolTable() = default;
|
||||
// 添加调试方法
|
||||
size_t getScopeCount() const { return active_scope_stack_.size(); }
|
||||
|
||||
void dump() const {
|
||||
std::cerr << "=== SymbolTable Dump ===" << std::endl;
|
||||
for (size_t i = 0; i < scopes_.size(); ++i) {
|
||||
std::cerr << "Scope " << i << " (depth=" << i << ")";
|
||||
bool active = std::find(active_scope_stack_.begin(), active_scope_stack_.end(), i) != active_scope_stack_.end();
|
||||
std::cerr << (active ? " [active]" : " [inactive]") << std::endl;
|
||||
for (const auto& [name, sym] : scopes_[i]) {
|
||||
std::cerr << " " << name
|
||||
<< " (kind=" << (int)sym.kind
|
||||
<< ", level=" << sym.scope_level << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----- 作用域管理 -----
|
||||
void enterScope(); // 进入新作用域
|
||||
void exitScope(); // 退出当前作用域
|
||||
int currentScopeLevel() const { return static_cast<int>(active_scope_stack_.size()) - 1; }
|
||||
|
||||
// ----- 符号操作(推荐使用)-----
|
||||
bool addSymbol(const Symbol& sym); // 添加符号到当前作用域
|
||||
Symbol* lookup(const std::string& name); // 从当前作用域向外查找
|
||||
Symbol* lookupCurrent(const std::string& name); // 仅在当前作用域查找
|
||||
const Symbol* lookup(const std::string& name) const;
|
||||
const Symbol* lookupCurrent(const std::string& name) const;
|
||||
const Symbol* lookupAll(const std::string& name) const; // 所有作用域查找,包括已结束的作用域
|
||||
const Symbol* lookupByVarDef(const SysYParser::VarDefContext* decl) const; // 通过定义节点查找符号
|
||||
const Symbol* lookupByConstDef(const SysYParser::ConstDefContext* decl) const; // 通过常量定义节点查找符号
|
||||
|
||||
// ----- 与原接口兼容(保留原有功能)-----
|
||||
void Add(const std::string& name, SysYParser::VarDefContext* decl);
|
||||
bool Contains(const std::string& name) const;
|
||||
SysYParser::VarDefContext* Lookup(const std::string& name) const;
|
||||
|
||||
// ----- 辅助函数:从语法树节点构造 Type -----
|
||||
static std::shared_ptr<ir::Type> getTypeFromFuncDef(SysYParser::FuncDefContext* ctx);
|
||||
|
||||
void registerBuiltinFunctions();
|
||||
|
||||
// 对常量表达式求值(返回整数值,用于数组维度等)
|
||||
int EvaluateConstExp(SysYParser::ConstExpContext* ctx) const;
|
||||
|
||||
// 对常量表达式求值(返回浮点值,用于全局初始化)
|
||||
float EvaluateConstExpFloat(SysYParser::ConstExpContext* ctx) const;
|
||||
|
||||
// 对常量初始化列表求值,返回一系列常量值(扁平化)
|
||||
struct ConstValue {
|
||||
enum Kind { INT, FLOAT };
|
||||
Kind kind;
|
||||
union {
|
||||
int int_val;
|
||||
float float_val;
|
||||
};
|
||||
};
|
||||
void flattenInit(SysYParser::ConstInitValContext* ctx,
|
||||
std::vector<ConstValue>& out,
|
||||
std::shared_ptr<ir::Type> base_type) const;
|
||||
std::vector<ConstValue> EvaluateConstInitVal(
|
||||
SysYParser::ConstInitValContext* ctx,
|
||||
const std::vector<int>& dims,
|
||||
std::shared_ptr<ir::Type> base_type) const;
|
||||
|
||||
int EvaluateConstExpression(SysYParser::ExpContext* ctx) const;
|
||||
|
||||
private:
|
||||
// 作用域栈:每个元素是一个从名字到符号的映射
|
||||
std::vector<std::unordered_map<std::string, Symbol>> scopes_;
|
||||
std::vector<size_t> active_scope_stack_;
|
||||
|
||||
static constexpr int GLOBAL_SCOPE = 0; // 全局作用域索引
|
||||
|
||||
ConstValue EvaluateAddExp(SysYParser::AddExpContext* ctx) const;
|
||||
ConstValue EvaluateMulExp(SysYParser::MulExpContext* ctx) const;
|
||||
ConstValue EvaluateUnaryExp(SysYParser::UnaryExpContext* ctx) const;
|
||||
ConstValue EvaluatePrimaryExp(SysYParser::PrimaryExpContext* ctx) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, SysYParser::VarDefContext*> table_;
|
||||
std::shared_ptr<ir::Type> getTypeFromVarDef(SysYParser::VarDefContext* ctx) const;
|
||||
};
|
||||
|
||||
Binary file not shown.
@ -0,0 +1,492 @@
|
||||
; ModuleID = 'optimized.bc'
|
||||
source_filename = "./build/test_compiler/performance/03_sort1.ll"
|
||||
|
||||
@a = global [30000010 x i32] zeroinitializer
|
||||
@ans = local_unnamed_addr global i32 0
|
||||
|
||||
declare i32 @getarray(ptr) local_unnamed_addr
|
||||
|
||||
declare void @putint(i32) local_unnamed_addr
|
||||
|
||||
declare void @putch(i32) local_unnamed_addr
|
||||
|
||||
declare void @starttime() local_unnamed_addr
|
||||
|
||||
declare void @stoptime() local_unnamed_addr
|
||||
|
||||
declare ptr @sysy_alloc_i32(i32) local_unnamed_addr
|
||||
|
||||
declare void @sysy_free_i32(ptr) local_unnamed_addr
|
||||
|
||||
declare void @sysy_zero_i32(ptr, i32) local_unnamed_addr
|
||||
|
||||
; Function Attrs: nofree norecurse nosync nounwind memory(argmem: read)
|
||||
define i32 @getMaxNum(i32 %n, ptr nocapture readonly %arr) local_unnamed_addr #0 {
|
||||
entry:
|
||||
%t95 = icmp sgt i32 %n, 0
|
||||
br i1 %t95, label %while.body.t5, label %while.exit.t6
|
||||
|
||||
while.body.t5: ; preds = %entry, %while.body.t5
|
||||
%t3_i.07 = phi i32 [ %t21, %while.body.t5 ], [ 0, %entry ]
|
||||
%t2_ret.06 = phi i32 [ %spec.select, %while.body.t5 ], [ 0, %entry ]
|
||||
%0 = zext nneg i32 %t3_i.07 to i64
|
||||
%t13 = getelementptr i32, ptr %arr, i64 %0
|
||||
%t14 = load i32, ptr %t13, align 4
|
||||
%spec.select = tail call i32 @llvm.smax.i32(i32 %t14, i32 %t2_ret.06)
|
||||
%t21 = add nuw nsw i32 %t3_i.07, 1
|
||||
%t9 = icmp slt i32 %t21, %n
|
||||
br i1 %t9, label %while.body.t5, label %while.exit.t6
|
||||
|
||||
while.exit.t6: ; preds = %while.body.t5, %entry
|
||||
%t2_ret.0.lcssa = phi i32 [ 0, %entry ], [ %spec.select, %while.body.t5 ]
|
||||
ret i32 %t2_ret.0.lcssa
|
||||
}
|
||||
|
||||
; Function Attrs: nofree norecurse nosync nounwind memory(none)
|
||||
define i32 @getNumPos(i32 %num, i32 %pos) local_unnamed_addr #1 {
|
||||
entry:
|
||||
%t333 = icmp sgt i32 %pos, 0
|
||||
br i1 %t333, label %while.body.t29, label %while.exit.t30
|
||||
|
||||
while.body.t29: ; preds = %entry, %while.body.t29
|
||||
%t27_i.05 = phi i32 [ %t37, %while.body.t29 ], [ 0, %entry ]
|
||||
%t24.04 = phi i32 [ %t35, %while.body.t29 ], [ %num, %entry ]
|
||||
%t35 = sdiv i32 %t24.04, 16
|
||||
%t37 = add nuw nsw i32 %t27_i.05, 1
|
||||
%t33 = icmp slt i32 %t37, %pos
|
||||
br i1 %t33, label %while.body.t29, label %while.exit.t30
|
||||
|
||||
while.exit.t30: ; preds = %while.body.t29, %entry
|
||||
%t24.0.lcssa = phi i32 [ %num, %entry ], [ %t35, %while.body.t29 ]
|
||||
%t39 = srem i32 %t24.0.lcssa, 16
|
||||
ret i32 %t39
|
||||
}
|
||||
|
||||
define void @radixSort(i32 %bitround, ptr nocapture %a, i32 %l, i32 %r) local_unnamed_addr {
|
||||
entry:
|
||||
%t43 = tail call ptr @sysy_alloc_i32(i32 16)
|
||||
tail call void @sysy_zero_i32(ptr %t43, i32 16)
|
||||
%t46 = tail call ptr @sysy_alloc_i32(i32 16)
|
||||
tail call void @sysy_zero_i32(ptr %t46, i32 16)
|
||||
%t48 = tail call ptr @sysy_alloc_i32(i32 16)
|
||||
tail call void @sysy_zero_i32(ptr %t48, i32 16)
|
||||
%t54 = icmp eq i32 %bitround, -1
|
||||
%t56 = add i32 %l, 1
|
||||
%t58 = icmp sge i32 %t56, %r
|
||||
%t59 = or i1 %t54, %t58
|
||||
br i1 %t59, label %cleanup.t44, label %while.cond.t62.preheader
|
||||
|
||||
while.cond.t62.preheader: ; preds = %entry
|
||||
%t6796 = icmp slt i32 %l, %r
|
||||
br i1 %t6796, label %while.body.t63.lr.ph, label %while.exit.t64
|
||||
|
||||
while.body.t63.lr.ph: ; preds = %while.cond.t62.preheader
|
||||
%t333.i = icmp sgt i32 %bitround, 0
|
||||
br label %while.body.t63
|
||||
|
||||
cleanup.t44: ; preds = %merge.t196, %entry
|
||||
tail call void @sysy_free_i32(ptr %t48)
|
||||
tail call void @sysy_free_i32(ptr %t46)
|
||||
tail call void @sysy_free_i32(ptr %t43)
|
||||
ret void
|
||||
|
||||
while.body.t63: ; preds = %while.body.t63.lr.ph, %getNumPos.exit28
|
||||
%storemerge97 = phi i32 [ %l, %while.body.t63.lr.ph ], [ %t83, %getNumPos.exit28 ]
|
||||
%0 = sext i32 %storemerge97 to i64
|
||||
%t69 = getelementptr i32, ptr %a, i64 %0
|
||||
%t70 = load i32, ptr %t69, align 4
|
||||
br i1 %t333.i, label %while.body.t29.i, label %getNumPos.exit.thread
|
||||
|
||||
getNumPos.exit.thread: ; preds = %while.body.t63
|
||||
%t39.i80 = srem i32 %t70, 16
|
||||
%1 = sext i32 %t39.i80 to i64
|
||||
%t7381 = getelementptr i32, ptr %t48, i64 %1
|
||||
%t7482 = load i32, ptr %t7381, align 4
|
||||
br label %getNumPos.exit28
|
||||
|
||||
while.body.t29.i: ; preds = %while.body.t63, %while.body.t29.i
|
||||
%t27_i.05.i = phi i32 [ %t37.i, %while.body.t29.i ], [ 0, %while.body.t63 ]
|
||||
%t24.04.i = phi i32 [ %t35.i, %while.body.t29.i ], [ %t70, %while.body.t63 ]
|
||||
%t35.i = sdiv i32 %t24.04.i, 16
|
||||
%t37.i = add nuw nsw i32 %t27_i.05.i, 1
|
||||
%t33.i = icmp slt i32 %t37.i, %bitround
|
||||
br i1 %t33.i, label %while.body.t29.i, label %getNumPos.exit
|
||||
|
||||
getNumPos.exit: ; preds = %while.body.t29.i
|
||||
%t39.i = srem i32 %t35.i, 16
|
||||
%2 = sext i32 %t39.i to i64
|
||||
%t73 = getelementptr i32, ptr %t48, i64 %2
|
||||
%t74 = load i32, ptr %t73, align 4
|
||||
br label %while.body.t29.i22
|
||||
|
||||
while.body.t29.i22: ; preds = %getNumPos.exit, %while.body.t29.i22
|
||||
%t27_i.05.i23 = phi i32 [ %t37.i26, %while.body.t29.i22 ], [ 0, %getNumPos.exit ]
|
||||
%t24.04.i24 = phi i32 [ %t35.i25, %while.body.t29.i22 ], [ %t70, %getNumPos.exit ]
|
||||
%t35.i25 = sdiv i32 %t24.04.i24, 16
|
||||
%t37.i26 = add nuw nsw i32 %t27_i.05.i23, 1
|
||||
%t33.i27 = icmp slt i32 %t37.i26, %bitround
|
||||
br i1 %t33.i27, label %while.body.t29.i22, label %getNumPos.exit28.loopexit
|
||||
|
||||
getNumPos.exit28.loopexit: ; preds = %while.body.t29.i22
|
||||
%.pre114 = srem i32 %t35.i25, 16
|
||||
%.pre115 = sext i32 %.pre114 to i64
|
||||
br label %getNumPos.exit28
|
||||
|
||||
getNumPos.exit28: ; preds = %getNumPos.exit28.loopexit, %getNumPos.exit.thread
|
||||
%.pre-phi116 = phi i64 [ %.pre115, %getNumPos.exit28.loopexit ], [ %1, %getNumPos.exit.thread ]
|
||||
%t7584.in = phi i32 [ %t74, %getNumPos.exit28.loopexit ], [ %t7482, %getNumPos.exit.thread ]
|
||||
%t7584 = add i32 %t7584.in, 1
|
||||
%t81 = getelementptr i32, ptr %t48, i64 %.pre-phi116
|
||||
store i32 %t7584, ptr %t81, align 4
|
||||
%t83 = add nsw i32 %storemerge97, 1
|
||||
%t67 = icmp slt i32 %t83, %r
|
||||
br i1 %t67, label %while.body.t63, label %while.exit.t64
|
||||
|
||||
while.exit.t64: ; preds = %getNumPos.exit28, %while.cond.t62.preheader
|
||||
store i32 %l, ptr %t43, align 4
|
||||
%t88 = load i32, ptr %t48, align 4
|
||||
%t89 = add i32 %t88, %l
|
||||
store i32 %t89, ptr %t46, align 4
|
||||
%invariant.gep = getelementptr i32, ptr %t46, i64 -1
|
||||
%t101 = getelementptr i32, ptr %t43, i64 1
|
||||
store i32 %t89, ptr %t101, align 4
|
||||
%t106 = getelementptr i32, ptr %t48, i64 1
|
||||
%t107 = load i32, ptr %t106, align 4
|
||||
%t108 = add i32 %t107, %t89
|
||||
%t110 = getelementptr i32, ptr %t46, i64 1
|
||||
store i32 %t108, ptr %t110, align 4
|
||||
%t101.1 = getelementptr i32, ptr %t43, i64 2
|
||||
store i32 %t108, ptr %t101.1, align 4
|
||||
%t106.1 = getelementptr i32, ptr %t48, i64 2
|
||||
%t107.1 = load i32, ptr %t106.1, align 4
|
||||
%t108.1 = add i32 %t107.1, %t108
|
||||
%t110.1 = getelementptr i32, ptr %t46, i64 2
|
||||
store i32 %t108.1, ptr %t110.1, align 4
|
||||
%t101.2 = getelementptr i32, ptr %t43, i64 3
|
||||
store i32 %t108.1, ptr %t101.2, align 4
|
||||
%t106.2 = getelementptr i32, ptr %t48, i64 3
|
||||
%t107.2 = load i32, ptr %t106.2, align 4
|
||||
%t108.2 = add i32 %t107.2, %t108.1
|
||||
%t110.2 = getelementptr i32, ptr %t46, i64 3
|
||||
store i32 %t108.2, ptr %t110.2, align 4
|
||||
%t101.3 = getelementptr i32, ptr %t43, i64 4
|
||||
store i32 %t108.2, ptr %t101.3, align 4
|
||||
%t106.3 = getelementptr i32, ptr %t48, i64 4
|
||||
%t107.3 = load i32, ptr %t106.3, align 4
|
||||
%t108.3 = add i32 %t107.3, %t108.2
|
||||
%t110.3 = getelementptr i32, ptr %t46, i64 4
|
||||
store i32 %t108.3, ptr %t110.3, align 4
|
||||
%t101.4 = getelementptr i32, ptr %t43, i64 5
|
||||
store i32 %t108.3, ptr %t101.4, align 4
|
||||
%t106.4 = getelementptr i32, ptr %t48, i64 5
|
||||
%t107.4 = load i32, ptr %t106.4, align 4
|
||||
%t108.4 = add i32 %t107.4, %t108.3
|
||||
%t110.4 = getelementptr i32, ptr %t46, i64 5
|
||||
store i32 %t108.4, ptr %t110.4, align 4
|
||||
%t101.5 = getelementptr i32, ptr %t43, i64 6
|
||||
store i32 %t108.4, ptr %t101.5, align 4
|
||||
%t106.5 = getelementptr i32, ptr %t48, i64 6
|
||||
%t107.5 = load i32, ptr %t106.5, align 4
|
||||
%t108.5 = add i32 %t107.5, %t108.4
|
||||
%t110.5 = getelementptr i32, ptr %t46, i64 6
|
||||
store i32 %t108.5, ptr %t110.5, align 4
|
||||
%t101.6 = getelementptr i32, ptr %t43, i64 7
|
||||
store i32 %t108.5, ptr %t101.6, align 4
|
||||
%t106.6 = getelementptr i32, ptr %t48, i64 7
|
||||
%t107.6 = load i32, ptr %t106.6, align 4
|
||||
%t108.6 = add i32 %t107.6, %t108.5
|
||||
%t110.6 = getelementptr i32, ptr %t46, i64 7
|
||||
store i32 %t108.6, ptr %t110.6, align 4
|
||||
%t101.7 = getelementptr i32, ptr %t43, i64 8
|
||||
store i32 %t108.6, ptr %t101.7, align 4
|
||||
%t106.7 = getelementptr i32, ptr %t48, i64 8
|
||||
%t107.7 = load i32, ptr %t106.7, align 4
|
||||
%t108.7 = add i32 %t107.7, %t108.6
|
||||
%t110.7 = getelementptr i32, ptr %t46, i64 8
|
||||
store i32 %t108.7, ptr %t110.7, align 4
|
||||
%t101.8 = getelementptr i32, ptr %t43, i64 9
|
||||
store i32 %t108.7, ptr %t101.8, align 4
|
||||
%t106.8 = getelementptr i32, ptr %t48, i64 9
|
||||
%t107.8 = load i32, ptr %t106.8, align 4
|
||||
%t108.8 = add i32 %t107.8, %t108.7
|
||||
%t110.8 = getelementptr i32, ptr %t46, i64 9
|
||||
store i32 %t108.8, ptr %t110.8, align 4
|
||||
%t101.9 = getelementptr i32, ptr %t43, i64 10
|
||||
store i32 %t108.8, ptr %t101.9, align 4
|
||||
%t106.9 = getelementptr i32, ptr %t48, i64 10
|
||||
%t107.9 = load i32, ptr %t106.9, align 4
|
||||
%t108.9 = add i32 %t107.9, %t108.8
|
||||
%t110.9 = getelementptr i32, ptr %t46, i64 10
|
||||
store i32 %t108.9, ptr %t110.9, align 4
|
||||
%t101.10 = getelementptr i32, ptr %t43, i64 11
|
||||
store i32 %t108.9, ptr %t101.10, align 4
|
||||
%t106.10 = getelementptr i32, ptr %t48, i64 11
|
||||
%t107.10 = load i32, ptr %t106.10, align 4
|
||||
%t108.10 = add i32 %t107.10, %t108.9
|
||||
%t110.10 = getelementptr i32, ptr %t46, i64 11
|
||||
store i32 %t108.10, ptr %t110.10, align 4
|
||||
%t101.11 = getelementptr i32, ptr %t43, i64 12
|
||||
store i32 %t108.10, ptr %t101.11, align 4
|
||||
%t106.11 = getelementptr i32, ptr %t48, i64 12
|
||||
%t107.11 = load i32, ptr %t106.11, align 4
|
||||
%t108.11 = add i32 %t107.11, %t108.10
|
||||
%t110.11 = getelementptr i32, ptr %t46, i64 12
|
||||
store i32 %t108.11, ptr %t110.11, align 4
|
||||
%t101.12 = getelementptr i32, ptr %t43, i64 13
|
||||
store i32 %t108.11, ptr %t101.12, align 4
|
||||
%t106.12 = getelementptr i32, ptr %t48, i64 13
|
||||
%t107.12 = load i32, ptr %t106.12, align 4
|
||||
%t108.12 = add i32 %t107.12, %t108.11
|
||||
%t110.12 = getelementptr i32, ptr %t46, i64 13
|
||||
store i32 %t108.12, ptr %t110.12, align 4
|
||||
%t101.13 = getelementptr i32, ptr %t43, i64 14
|
||||
store i32 %t108.12, ptr %t101.13, align 4
|
||||
%t106.13 = getelementptr i32, ptr %t48, i64 14
|
||||
%t107.13 = load i32, ptr %t106.13, align 4
|
||||
%t108.13 = add i32 %t107.13, %t108.12
|
||||
%t110.13 = getelementptr i32, ptr %t46, i64 14
|
||||
store i32 %t108.13, ptr %t110.13, align 4
|
||||
%t101.14 = getelementptr i32, ptr %t43, i64 15
|
||||
store i32 %t108.13, ptr %t101.14, align 4
|
||||
%t106.14 = getelementptr i32, ptr %t48, i64 15
|
||||
%t107.14 = load i32, ptr %t106.14, align 4
|
||||
%t108.14 = add i32 %t107.14, %t108.13
|
||||
%t110.14 = getelementptr i32, ptr %t46, i64 15
|
||||
store i32 %t108.14, ptr %t110.14, align 4
|
||||
%t333.i29 = icmp sgt i32 %bitround, 0
|
||||
br label %while.cond.t118.preheader
|
||||
|
||||
while.cond.t118.preheader: ; preds = %while.exit.t64, %while.exit.t120
|
||||
%storemerge17104 = phi i32 [ 0, %while.exit.t64 ], [ %t180, %while.exit.t120 ]
|
||||
%3 = zext nneg i32 %storemerge17104 to i64
|
||||
%t122 = getelementptr i32, ptr %t43, i64 %3
|
||||
%t125 = getelementptr i32, ptr %t46, i64 %3
|
||||
%t123100 = load i32, ptr %t122, align 4
|
||||
%t126101 = load i32, ptr %t125, align 4
|
||||
%t127102 = icmp slt i32 %t123100, %t126101
|
||||
br i1 %t127102, label %while.body.t119, label %while.exit.t120
|
||||
|
||||
while.body.t191.peel.next: ; preds = %while.exit.t120
|
||||
store i32 %l, ptr %t43, align 4
|
||||
%t187 = load i32, ptr %t48, align 4
|
||||
%t188 = add i32 %t187, %l
|
||||
store i32 %t188, ptr %t46, align 4
|
||||
%t215 = add i32 %bitround, -1
|
||||
%t218.peel.pre = load i32, ptr %t43, align 4
|
||||
tail call void @radixSort(i32 %t215, ptr %a, i32 %t218.peel.pre, i32 %t188)
|
||||
br label %merge.t196
|
||||
|
||||
while.body.t119: ; preds = %while.cond.t118.preheader, %while.exit.t136
|
||||
%t123103 = phi i32 [ %t176, %while.exit.t136 ], [ %t123100, %while.cond.t118.preheader ]
|
||||
%4 = sext i32 %t123103 to i64
|
||||
%t132 = getelementptr i32, ptr %a, i64 %4
|
||||
%t133 = load i32, ptr %t132, align 4
|
||||
br label %while.cond.t134
|
||||
|
||||
while.exit.t120: ; preds = %while.exit.t136, %while.cond.t118.preheader
|
||||
%t180 = add nuw nsw i32 %storemerge17104, 1
|
||||
%t117 = icmp ult i32 %storemerge17104, 15
|
||||
br i1 %t117, label %while.cond.t118.preheader, label %while.body.t191.peel.next
|
||||
|
||||
while.cond.t134: ; preds = %getNumPos.exit78, %while.body.t119
|
||||
%t15099 = phi i32 [ %t150129, %getNumPos.exit78 ], [ %t133, %while.body.t119 ]
|
||||
br i1 %t333.i29, label %while.body.t29.i32, label %getNumPos.exit38.thread
|
||||
|
||||
while.body.t29.i32: ; preds = %while.cond.t134, %while.body.t29.i32
|
||||
%t27_i.05.i33 = phi i32 [ %t37.i36, %while.body.t29.i32 ], [ 0, %while.cond.t134 ]
|
||||
%t24.04.i34 = phi i32 [ %t35.i35, %while.body.t29.i32 ], [ %t15099, %while.cond.t134 ]
|
||||
%t35.i35 = sdiv i32 %t24.04.i34, 16
|
||||
%t37.i36 = add nuw nsw i32 %t27_i.05.i33, 1
|
||||
%t33.i37 = icmp slt i32 %t37.i36, %bitround
|
||||
br i1 %t33.i37, label %while.body.t29.i32, label %getNumPos.exit38
|
||||
|
||||
getNumPos.exit38: ; preds = %while.body.t29.i32
|
||||
%t39.i31 = srem i32 %t35.i35, 16
|
||||
%t141.not = icmp eq i32 %t39.i31, %storemerge17104
|
||||
br i1 %t141.not, label %while.exit.t136, label %while.body.t29.i42
|
||||
|
||||
getNumPos.exit38.thread: ; preds = %while.cond.t134
|
||||
%t39.i3186 = srem i32 %t15099, 16
|
||||
%t141.not87 = icmp eq i32 %t39.i3186, %storemerge17104
|
||||
br i1 %t141.not87, label %while.exit.t136, label %getNumPos.exit48.thread
|
||||
|
||||
getNumPos.exit48.thread: ; preds = %getNumPos.exit38.thread
|
||||
%5 = sext i32 %t39.i3186 to i64
|
||||
%t147125 = getelementptr i32, ptr %t43, i64 %5
|
||||
%t148126 = load i32, ptr %t147125, align 4
|
||||
%6 = sext i32 %t148126 to i64
|
||||
%t149127 = getelementptr i32, ptr %a, i64 %6
|
||||
%t150128 = load i32, ptr %t149127, align 4
|
||||
br label %getNumPos.exit68.thread
|
||||
|
||||
while.body.t29.i42: ; preds = %getNumPos.exit38, %while.body.t29.i42
|
||||
%t27_i.05.i43 = phi i32 [ %t37.i46, %while.body.t29.i42 ], [ 0, %getNumPos.exit38 ]
|
||||
%t24.04.i44 = phi i32 [ %t35.i45, %while.body.t29.i42 ], [ %t15099, %getNumPos.exit38 ]
|
||||
%t35.i45 = sdiv i32 %t24.04.i44, 16
|
||||
%t37.i46 = add nuw nsw i32 %t27_i.05.i43, 1
|
||||
%t33.i47 = icmp slt i32 %t37.i46, %bitround
|
||||
br i1 %t33.i47, label %while.body.t29.i42, label %getNumPos.exit48
|
||||
|
||||
getNumPos.exit48: ; preds = %while.body.t29.i42
|
||||
%.pre117 = srem i32 %t35.i45, 16
|
||||
%7 = sext i32 %.pre117 to i64
|
||||
%t147 = getelementptr i32, ptr %t43, i64 %7
|
||||
%t148 = load i32, ptr %t147, align 4
|
||||
%8 = sext i32 %t148 to i64
|
||||
%t149 = getelementptr i32, ptr %a, i64 %8
|
||||
%t150 = load i32, ptr %t149, align 4
|
||||
br i1 %t333.i29, label %while.body.t29.i52, label %getNumPos.exit68.thread
|
||||
|
||||
while.body.t29.i52: ; preds = %getNumPos.exit48, %while.body.t29.i52
|
||||
%t27_i.05.i53 = phi i32 [ %t37.i56, %while.body.t29.i52 ], [ 0, %getNumPos.exit48 ]
|
||||
%t24.04.i54 = phi i32 [ %t35.i55, %while.body.t29.i52 ], [ %t15099, %getNumPos.exit48 ]
|
||||
%t35.i55 = sdiv i32 %t24.04.i54, 16
|
||||
%t37.i56 = add nuw nsw i32 %t27_i.05.i53, 1
|
||||
%t33.i57 = icmp slt i32 %t37.i56, %bitround
|
||||
br i1 %t33.i57, label %while.body.t29.i52, label %while.body.t29.i62.preheader
|
||||
|
||||
while.body.t29.i62.preheader: ; preds = %while.body.t29.i52
|
||||
%t39.i51 = srem i32 %t35.i55, 16
|
||||
%9 = sext i32 %t39.i51 to i64
|
||||
%t155 = getelementptr i32, ptr %t43, i64 %9
|
||||
%t156 = load i32, ptr %t155, align 4
|
||||
%10 = sext i32 %t156 to i64
|
||||
%t157 = getelementptr i32, ptr %a, i64 %10
|
||||
store i32 %t15099, ptr %t157, align 4
|
||||
br label %while.body.t29.i62
|
||||
|
||||
getNumPos.exit68.thread: ; preds = %getNumPos.exit48, %getNumPos.exit48.thread
|
||||
%t150130 = phi i32 [ %t150128, %getNumPos.exit48.thread ], [ %t150, %getNumPos.exit48 ]
|
||||
%t39.i51.c = srem i32 %t15099, 16
|
||||
%11 = sext i32 %t39.i51.c to i64
|
||||
%t155.c = getelementptr i32, ptr %t43, i64 %11
|
||||
%t156.c = load i32, ptr %t155.c, align 4
|
||||
%12 = sext i32 %t156.c to i64
|
||||
%t157.c = getelementptr i32, ptr %a, i64 %12
|
||||
store i32 %t15099, ptr %t157.c, align 4
|
||||
%t16191 = getelementptr i32, ptr %t43, i64 %11
|
||||
%t16292 = load i32, ptr %t16191, align 4
|
||||
br label %getNumPos.exit78
|
||||
|
||||
while.body.t29.i62: ; preds = %while.body.t29.i62.preheader, %while.body.t29.i62
|
||||
%t27_i.05.i63 = phi i32 [ %t37.i66, %while.body.t29.i62 ], [ 0, %while.body.t29.i62.preheader ]
|
||||
%t24.04.i64 = phi i32 [ %t35.i65, %while.body.t29.i62 ], [ %t15099, %while.body.t29.i62.preheader ]
|
||||
%t35.i65 = sdiv i32 %t24.04.i64, 16
|
||||
%t37.i66 = add nuw nsw i32 %t27_i.05.i63, 1
|
||||
%t33.i67 = icmp slt i32 %t37.i66, %bitround
|
||||
br i1 %t33.i67, label %while.body.t29.i62, label %getNumPos.exit68
|
||||
|
||||
getNumPos.exit68: ; preds = %while.body.t29.i62
|
||||
%t39.i61 = srem i32 %t35.i65, 16
|
||||
%13 = sext i32 %t39.i61 to i64
|
||||
%t161 = getelementptr i32, ptr %t43, i64 %13
|
||||
%t162 = load i32, ptr %t161, align 4
|
||||
br label %while.body.t29.i72
|
||||
|
||||
while.body.t29.i72: ; preds = %getNumPos.exit68, %while.body.t29.i72
|
||||
%t27_i.05.i73 = phi i32 [ %t37.i76, %while.body.t29.i72 ], [ 0, %getNumPos.exit68 ]
|
||||
%t24.04.i74 = phi i32 [ %t35.i75, %while.body.t29.i72 ], [ %t15099, %getNumPos.exit68 ]
|
||||
%t35.i75 = sdiv i32 %t24.04.i74, 16
|
||||
%t37.i76 = add nuw nsw i32 %t27_i.05.i73, 1
|
||||
%t33.i77 = icmp slt i32 %t37.i76, %bitround
|
||||
br i1 %t33.i77, label %while.body.t29.i72, label %getNumPos.exit78.loopexit
|
||||
|
||||
getNumPos.exit78.loopexit: ; preds = %while.body.t29.i72
|
||||
%.pre118 = srem i32 %t35.i75, 16
|
||||
%.pre119 = sext i32 %.pre118 to i64
|
||||
br label %getNumPos.exit78
|
||||
|
||||
getNumPos.exit78: ; preds = %getNumPos.exit78.loopexit, %getNumPos.exit68.thread
|
||||
%t150129 = phi i32 [ %t150, %getNumPos.exit78.loopexit ], [ %t150130, %getNumPos.exit68.thread ]
|
||||
%.pre-phi120 = phi i64 [ %.pre119, %getNumPos.exit78.loopexit ], [ %11, %getNumPos.exit68.thread ]
|
||||
%t16394.in = phi i32 [ %t162, %getNumPos.exit78.loopexit ], [ %t16292, %getNumPos.exit68.thread ]
|
||||
%t16394 = add i32 %t16394.in, 1
|
||||
%t167 = getelementptr i32, ptr %t43, i64 %.pre-phi120
|
||||
store i32 %t16394, ptr %t167, align 4
|
||||
br label %while.cond.t134
|
||||
|
||||
while.exit.t136: ; preds = %getNumPos.exit38.thread, %getNumPos.exit38
|
||||
%t171 = load i32, ptr %t122, align 4
|
||||
%14 = sext i32 %t171 to i64
|
||||
%t172 = getelementptr i32, ptr %a, i64 %14
|
||||
store i32 %t15099, ptr %t172, align 4
|
||||
%t175 = load i32, ptr %t122, align 4
|
||||
%t176 = add i32 %t175, 1
|
||||
store i32 %t176, ptr %t122, align 4
|
||||
%t126 = load i32, ptr %t125, align 4
|
||||
%t127 = icmp slt i32 %t176, %t126
|
||||
br i1 %t127, label %while.body.t119, label %while.exit.t120
|
||||
|
||||
merge.t196: ; preds = %while.body.t191.peel.next, %merge.t196
|
||||
%storemerge18107 = phi i32 [ 1, %while.body.t191.peel.next ], [ %t224, %merge.t196 ]
|
||||
%15 = zext nneg i32 %storemerge18107 to i64
|
||||
%gep106 = getelementptr i32, ptr %invariant.gep, i64 %15
|
||||
%t202 = load i32, ptr %gep106, align 4
|
||||
%t204 = getelementptr i32, ptr %t43, i64 %15
|
||||
store i32 %t202, ptr %t204, align 4
|
||||
%t209 = getelementptr i32, ptr %t48, i64 %15
|
||||
%t210 = load i32, ptr %t209, align 4
|
||||
%t211 = add i32 %t210, %t202
|
||||
%t213 = getelementptr i32, ptr %t46, i64 %15
|
||||
store i32 %t211, ptr %t213, align 4
|
||||
%t218.pre = load i32, ptr %t204, align 4
|
||||
tail call void @radixSort(i32 %t215, ptr %a, i32 %t218.pre, i32 %t211)
|
||||
%t224 = add nuw nsw i32 %storemerge18107, 1
|
||||
%t194 = icmp ult i32 %storemerge18107, 15
|
||||
br i1 %t194, label %merge.t196, label %cleanup.t44, !llvm.loop !0
|
||||
}
|
||||
|
||||
define noundef i32 @main() local_unnamed_addr {
|
||||
entry:
|
||||
%t231 = tail call i32 @getarray(ptr nonnull @a)
|
||||
tail call void @starttime()
|
||||
tail call void @radixSort(i32 8, ptr nonnull @a, i32 0, i32 %t231)
|
||||
%ans.promoted = load i32, ptr @ans, align 4
|
||||
%t2427 = icmp sgt i32 %t231, 0
|
||||
br i1 %t2427, label %while.body.t238, label %while.exit.t239
|
||||
|
||||
while.body.t238: ; preds = %entry, %while.body.t238
|
||||
%t236_i.09 = phi i32 [ %t254, %while.body.t238 ], [ 0, %entry ]
|
||||
%t25268 = phi i32 [ %t252, %while.body.t238 ], [ %ans.promoted, %entry ]
|
||||
%0 = zext nneg i32 %t236_i.09 to i64
|
||||
%t246 = getelementptr [30000010 x i32], ptr @a, i64 0, i64 %0
|
||||
%t247 = load i32, ptr %t246, align 4
|
||||
%t249 = add nuw i32 %t236_i.09, 2
|
||||
%t250 = srem i32 %t247, %t249
|
||||
%t251 = mul i32 %t250, %t236_i.09
|
||||
%t252 = add i32 %t251, %t25268
|
||||
%t254 = add nuw nsw i32 %t236_i.09, 1
|
||||
%t242 = icmp slt i32 %t254, %t231
|
||||
br i1 %t242, label %while.body.t238, label %while.cond.t237.while.exit.t239_crit_edge
|
||||
|
||||
while.cond.t237.while.exit.t239_crit_edge: ; preds = %while.body.t238
|
||||
store i32 %t252, ptr @ans, align 4
|
||||
br label %while.exit.t239
|
||||
|
||||
while.exit.t239: ; preds = %while.cond.t237.while.exit.t239_crit_edge, %entry
|
||||
%t257 = phi i32 [ %t252, %while.cond.t237.while.exit.t239_crit_edge ], [ %ans.promoted, %entry ]
|
||||
%t258 = icmp slt i32 %t257, 0
|
||||
br i1 %t258, label %then.t255, label %merge.t256
|
||||
|
||||
then.t255: ; preds = %while.exit.t239
|
||||
%t260 = sub i32 0, %t257
|
||||
store i32 %t260, ptr @ans, align 4
|
||||
br label %merge.t256
|
||||
|
||||
merge.t256: ; preds = %then.t255, %while.exit.t239
|
||||
tail call void @stoptime()
|
||||
%t262 = load i32, ptr @ans, align 4
|
||||
tail call void @putint(i32 %t262)
|
||||
tail call void @putch(i32 10)
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.smax.i32(i32, i32) #2
|
||||
|
||||
attributes #0 = { nofree norecurse nosync nounwind memory(argmem: read) }
|
||||
attributes #1 = { nofree norecurse nosync nounwind memory(none) }
|
||||
attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
|
||||
!0 = distinct !{!0, !1}
|
||||
!1 = !{!"llvm.loop.peeled.count", i32 1}
|
||||
@ -0,0 +1,98 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 测试脚本
|
||||
# 用法: ./run_tests.sh [--verbose] [<单个测试文件>]
|
||||
|
||||
COMPILER="./build/bin/compiler"
|
||||
TEST_DIR="test/test_case"
|
||||
RESULT_FILE="result.txt"
|
||||
VERBOSE=0
|
||||
|
||||
# 解析参数
|
||||
if [[ "$1" == "--verbose" ]]; then
|
||||
VERBOSE=1
|
||||
shift
|
||||
fi
|
||||
|
||||
# 检查编译器是否存在
|
||||
if [ ! -f "$COMPILER" ]; then
|
||||
echo "错误: 编译器未找到于 $COMPILER"
|
||||
echo "请先完成项目构建 (cmake 和 make)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 如果指定了单个文件,检查文件是否存在
|
||||
if [ -n "$1" ] && [ ! -f "$1" ]; then
|
||||
echo "错误: 文件 $1 不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 清空(或创建)结果文件
|
||||
> "$RESULT_FILE"
|
||||
|
||||
# 计数器
|
||||
total=0
|
||||
passed=0
|
||||
failed=0
|
||||
|
||||
echo "开始测试 ir out 解析..."
|
||||
echo "输出将保存到 $RESULT_FILE"
|
||||
echo "------------------------"
|
||||
|
||||
# 确定测试文件列表
|
||||
if [ -n "$1" ]; then
|
||||
# 使用提供的单个文件
|
||||
TEST_FILES=("$1")
|
||||
else
|
||||
# 收集所有 .sy 文件
|
||||
mapfile -t TEST_FILES < <(find "$TEST_DIR" -type f -name "*.sy" | sort)
|
||||
fi
|
||||
|
||||
for file in "${TEST_FILES[@]}"; do
|
||||
((total++))
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo "测试文件: $file"
|
||||
else
|
||||
echo -n "测试 $file ... "
|
||||
fi
|
||||
|
||||
echo "========== $file ==========" >> "$RESULT_FILE"
|
||||
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
# "$COMPILER" --emit-parse-tree "$file" 2>&1 | tee -a "$RESULT_FILE"
|
||||
"$COMPILER" --emit-ir "$file" 2>&1 | tee -a "$RESULT_FILE"
|
||||
result=${PIPESTATUS[0]}
|
||||
else
|
||||
# "$COMPILER" --emit-parse-tree "$file" >> "$RESULT_FILE" 2>&1
|
||||
"$COMPILER" --emit-ir "$file" >> "$RESULT_FILE" 2>&1
|
||||
result=$?
|
||||
fi
|
||||
|
||||
echo "" >> "$RESULT_FILE"
|
||||
|
||||
if [ $result -eq 0 ]; then
|
||||
if [ $VERBOSE -eq 0 ]; then
|
||||
echo "通过"
|
||||
fi
|
||||
((passed++))
|
||||
else
|
||||
if [ $VERBOSE -eq 0 ]; then
|
||||
echo "失败"
|
||||
else
|
||||
echo ">>> 解析失败: $file"
|
||||
fi
|
||||
((failed++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "------------------------"
|
||||
echo "总计: $total"
|
||||
echo "通过: $passed"
|
||||
echo "失败: $failed"
|
||||
echo "详细输出已保存至 $RESULT_FILE"
|
||||
|
||||
if [ $failed -gt 0 ]; then
|
||||
exit 1
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
@ -1,11 +1,195 @@
|
||||
// GlobalValue 占位实现:
|
||||
// - 具体的全局初始化器、打印和链接语义需要自行补全
|
||||
|
||||
// ir/GlobalValue.cpp
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ir {
|
||||
|
||||
namespace {
|
||||
|
||||
ConstantValue* GetScalarZeroConstant(const Type& type) {
|
||||
if (type.IsInt32()) {
|
||||
static ConstantInt* zero_i32 = new ConstantInt(Type::GetInt32Type(), 0);
|
||||
return zero_i32;
|
||||
}
|
||||
if (type.IsFloat()) {
|
||||
static ConstantFloat* zero_f32 = new ConstantFloat(Type::GetFloatType(), 0.0f);
|
||||
return zero_f32;
|
||||
}
|
||||
if (type.IsInt1()) {
|
||||
static ConstantInt* zero_i1 = new ConstantInt(Type::GetInt1Type(), 0);
|
||||
return zero_i1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GlobalValue::GlobalValue(std::shared_ptr<Type> ty, std::string name)
|
||||
: User(std::move(ty), std::move(name)) {}
|
||||
|
||||
} // namespace ir
|
||||
void GlobalValue::SetInitializer(ConstantValue* init) {
|
||||
if (!init) {
|
||||
throw std::runtime_error("GlobalValue::SetInitializer: init is null");
|
||||
}
|
||||
|
||||
// 获取实际的值类型(用于类型检查)
|
||||
std::shared_ptr<Type> value_type = GetValueType();
|
||||
|
||||
// 类型检查
|
||||
bool type_match = CheckTypeCompatibility(value_type, init);
|
||||
|
||||
if (!type_match) {
|
||||
throw std::runtime_error("GlobalValue::SetInitializer: type mismatch");
|
||||
}
|
||||
|
||||
initializer_.clear();
|
||||
initializer_.push_back(init);
|
||||
}
|
||||
|
||||
void GlobalValue::SetInitializer(const std::vector<ConstantValue*>& init) {
|
||||
if (init.empty()) {
|
||||
initializer_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取实际的值类型
|
||||
std::shared_ptr<Type> value_type = GetValueType();
|
||||
|
||||
// 类型检查
|
||||
if (value_type->IsArray()) {
|
||||
auto* array_ty = static_cast<ArrayType*>(value_type.get());
|
||||
size_t array_size = array_ty->GetElementCount();
|
||||
|
||||
if (init.size() > array_size) {
|
||||
throw std::runtime_error("GlobalValue::SetInitializer: too many initializers");
|
||||
}
|
||||
|
||||
// 检查每个初始化值的类型
|
||||
auto* elem_type = array_ty->GetElementType().get();
|
||||
for (size_t i = 0; i < init.size(); ++i) {
|
||||
auto* elem = init[i];
|
||||
if (!elem) {
|
||||
throw std::runtime_error("GlobalValue::SetInitializer: null initializer at index " + std::to_string(i));
|
||||
}
|
||||
|
||||
bool elem_match = false;
|
||||
if (elem_type->IsInt32() && elem->GetType()->IsInt32()) {
|
||||
elem_match = true;
|
||||
} else if (elem_type->IsFloat() && elem->GetType()->IsFloat()) {
|
||||
elem_match = true;
|
||||
} else if (elem_type->IsInt1() && elem->GetType()->IsInt1()) {
|
||||
elem_match = true;
|
||||
}
|
||||
|
||||
if (!elem_match) {
|
||||
throw std::runtime_error("GlobalValue::SetInitializer: element type mismatch at index " + std::to_string(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (value_type->IsInt32() || value_type->IsFloat() || value_type->IsInt1()) {
|
||||
if (init.size() != 1) {
|
||||
throw std::runtime_error("GlobalValue::SetInitializer: scalar requires exactly one initializer");
|
||||
}
|
||||
|
||||
if (!init[0]) {
|
||||
throw std::runtime_error("GlobalValue::SetInitializer: null initializer");
|
||||
}
|
||||
|
||||
if ((value_type->IsInt32() && !init[0]->GetType()->IsInt32()) ||
|
||||
(value_type->IsFloat() && !init[0]->GetType()->IsFloat()) ||
|
||||
(value_type->IsInt1() && !init[0]->GetType()->IsInt1())) {
|
||||
throw std::runtime_error("GlobalValue::SetInitializer: type mismatch");
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("GlobalValue::SetInitializer: unsupported type");
|
||||
}
|
||||
|
||||
initializer_ = init;
|
||||
}
|
||||
|
||||
// 辅助方法:获取实际的值类型(处理指针包装)
|
||||
std::shared_ptr<Type> GlobalValue::GetValueType() const {
|
||||
if (GetType()->IsPtrInt32()) {
|
||||
return Type::GetInt32Type();
|
||||
} else if (GetType()->IsPtrFloat()) {
|
||||
return Type::GetFloatType();
|
||||
} else if (GetType()->IsPtrInt1()) {
|
||||
return Type::GetInt1Type();
|
||||
}
|
||||
return GetType();
|
||||
}
|
||||
|
||||
// 辅助方法:检查类型兼容性
|
||||
bool GlobalValue::CheckTypeCompatibility(std::shared_ptr<Type> value_type,
|
||||
ConstantValue* init) const {
|
||||
// 检查标量类型
|
||||
if (value_type->IsInt32() && init->GetType()->IsInt32()) {
|
||||
return true;
|
||||
} else if (value_type->IsFloat() && init->GetType()->IsFloat()) {
|
||||
return true;
|
||||
} else if (value_type->IsInt1() && init->GetType()->IsInt1()) {
|
||||
return true;
|
||||
}
|
||||
// 检查数组类型:允许用单个标量初始化整个数组
|
||||
else if (value_type->IsArray()) {
|
||||
auto* array_ty = static_cast<ArrayType*>(value_type.get());
|
||||
auto* elem_type = array_ty->GetElementType().get();
|
||||
|
||||
if (elem_type->IsInt32() && init->GetType()->IsInt32()) {
|
||||
return true;
|
||||
} else if (elem_type->IsFloat() && init->GetType()->IsFloat()) {
|
||||
return true;
|
||||
} else if (elem_type->IsInt1() && init->GetType()->IsInt1()) {
|
||||
return true;
|
||||
}
|
||||
// 也可以允许 ConstantArray 作为初始化器
|
||||
else if (init->GetType()->IsArray()) {
|
||||
auto* init_array = static_cast<ConstantArray*>(init);
|
||||
return init_array->IsValid();
|
||||
}
|
||||
}
|
||||
// 检查指针类型(用于数组参数)
|
||||
else if (value_type->IsPtrInt32() && init->GetType()->IsInt32()) {
|
||||
return true;
|
||||
} else if (value_type->IsPtrFloat() && init->GetType()->IsFloat()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加获取数组元素的便捷方法
|
||||
ConstantValue* GlobalValue::GetArrayElement(size_t index) const {
|
||||
if (!GetType()->IsArray()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* array_ty = dynamic_cast<ArrayType*>(GetType().get());
|
||||
if (!array_ty) {
|
||||
return nullptr;
|
||||
}
|
||||
if (index >= static_cast<size_t>(array_ty->GetElementCount())) {
|
||||
return nullptr;
|
||||
}
|
||||
if (index >= initializer_.size()) {
|
||||
return GetScalarZeroConstant(*array_ty->GetElementType());
|
||||
}
|
||||
return initializer_[index];
|
||||
}
|
||||
|
||||
// 添加获取数组元素数量的方法
|
||||
size_t GlobalValue::GetArraySize() const {
|
||||
if (!IsArrayConstant()) {
|
||||
return 0;
|
||||
}
|
||||
return initializer_.size();
|
||||
}
|
||||
|
||||
// 添加判断是否为数组常量的方法
|
||||
bool GlobalValue::IsArrayConstant() const {
|
||||
return GetType()->IsArray() && !initializer_.empty();
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
@ -1,31 +1,219 @@
|
||||
// 当前仅支持 void、i32 和 i32*。
|
||||
#include "ir/IR.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace ir {
|
||||
|
||||
Type::Type(Kind k) : kind_(k) {}
|
||||
|
||||
size_t Type::Size() const {
|
||||
switch (kind_) {
|
||||
case Kind::Void: return 0;
|
||||
case Kind::Int32: return 4;
|
||||
case Kind::Float: return 4; // 单精度浮点 4 字节
|
||||
case Kind::PtrInt32: return 8; // 假设 64 位指针
|
||||
case Kind::PtrFloat: return 8;
|
||||
case Kind::Label: return 8; // 标签地址大小(指针大小)
|
||||
default: return 0; // 派生类应重写
|
||||
}
|
||||
}
|
||||
|
||||
size_t Type::Alignment() const {
|
||||
switch (kind_) {
|
||||
case Kind::Int32: return 4;
|
||||
case Kind::Float: return 4;
|
||||
case Kind::PtrInt32: return 8;
|
||||
case Kind::PtrFloat: return 8;
|
||||
case Kind::Label: return 8; // 与指针相同
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool Type::IsComplete() const {
|
||||
return kind_ != Kind::Void;
|
||||
}
|
||||
const std::shared_ptr<Type>& Type::GetVoidType() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Void);
|
||||
static const std::shared_ptr<Type> type = std::shared_ptr<Type>(new Type(Kind::Void));
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetInt32Type() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Int32);
|
||||
static const std::shared_ptr<Type> type = std::shared_ptr<Type>(new Type(Kind::Int32));
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetFloatType() {
|
||||
static const std::shared_ptr<Type> type(new Type(Kind::Float));
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetPtrInt32Type() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::PtrInt32);
|
||||
static const std::shared_ptr<Type> type = std::shared_ptr<Type>(new Type(Kind::PtrInt32));
|
||||
return type;
|
||||
}
|
||||
|
||||
Type::Kind Type::GetKind() const { return kind_; }
|
||||
const std::shared_ptr<Type>& Type::GetPtrFloatType() {
|
||||
static const std::shared_ptr<Type> type(new Type(Kind::PtrFloat));
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetLabelType() {
|
||||
static const std::shared_ptr<Type> type(new Type(Kind::Label));
|
||||
return type;
|
||||
}
|
||||
|
||||
// Int1 类型表示布尔值,通常用于比较指令的结果
|
||||
const std::shared_ptr<Type>& Type::GetInt1Type() {
|
||||
static const std::shared_ptr<Type> type = std::shared_ptr<Type>(new Type(Kind::Int1));
|
||||
return type;
|
||||
}
|
||||
// PtrInt1 类型表示指向 Int1 的指针,主要用于条件跳转等场景
|
||||
const std::shared_ptr<Type>& Type::GetPtrInt1Type() {
|
||||
static const std::shared_ptr<Type> type = std::shared_ptr<Type>(new Type(Kind::PtrInt1));
|
||||
return type;
|
||||
}
|
||||
|
||||
bool Type::IsVoid() const { return kind_ == Kind::Void; }
|
||||
// ---------- 数组类型缓存 ----------
|
||||
// 使用自定义键类型保证唯一性:元素类型指针 + 维度向量
|
||||
struct ArrayKey {
|
||||
const Type* elem_type;
|
||||
std::vector<int> dims;
|
||||
|
||||
bool Type::IsInt32() const { return kind_ == Kind::Int32; }
|
||||
bool operator==(const ArrayKey& other) const {
|
||||
return elem_type == other.elem_type && dims == other.dims;
|
||||
}
|
||||
};
|
||||
|
||||
bool Type::IsPtrInt32() const { return kind_ == Kind::PtrInt32; }
|
||||
struct ArrayKeyHash {
|
||||
std::size_t operator()(const ArrayKey& key) const {
|
||||
std::size_t h = std::hash<const Type*>{}(key.elem_type);
|
||||
for (int d : key.dims) {
|
||||
h ^= std::hash<int>{}(d) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
static std::unordered_map<ArrayKey, std::weak_ptr<ArrayType>, ArrayKeyHash>& GetArrayCache() {
|
||||
static std::unordered_map<ArrayKey, std::weak_ptr<ArrayType>, ArrayKeyHash> cache;
|
||||
return cache;
|
||||
}
|
||||
|
||||
std::shared_ptr<ArrayType> Type::GetArrayType(std::shared_ptr<Type> elem,
|
||||
std::vector<int> dims) {
|
||||
// 检查维度合法性
|
||||
for (int d : dims) {
|
||||
if (d <= 0) {
|
||||
// SysY 数组维度必须为正整数常量表达式,这里假设已检查
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayKey key{elem.get(), dims};
|
||||
auto& cache = GetArrayCache();
|
||||
auto it = cache.find(key);
|
||||
if (it != cache.end()) {
|
||||
auto ptr = it->second.lock();
|
||||
if (ptr) return ptr;
|
||||
}
|
||||
|
||||
auto arr = std::shared_ptr<ArrayType>(new ArrayType(std::move(elem), std::move(dims)));
|
||||
cache[key] = arr;
|
||||
return arr;
|
||||
}
|
||||
|
||||
// ---------- 函数类型缓存 ----------
|
||||
struct FunctionKey {
|
||||
const Type* return_type;
|
||||
std::vector<const Type*> param_types;
|
||||
|
||||
bool operator==(const FunctionKey& other) const {
|
||||
return return_type == other.return_type && param_types == other.param_types;
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionKeyHash {
|
||||
std::size_t operator()(const FunctionKey& key) const {
|
||||
std::size_t h = std::hash<const Type*>{}(key.return_type);
|
||||
for (const Type* t : key.param_types) {
|
||||
h ^= std::hash<const Type*>{}(t) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
static std::unordered_map<FunctionKey, std::weak_ptr<FunctionType>, FunctionKeyHash>& GetFunctionCache() {
|
||||
static std::unordered_map<FunctionKey, std::weak_ptr<FunctionType>, FunctionKeyHash> cache;
|
||||
return cache;
|
||||
}
|
||||
|
||||
std::shared_ptr<FunctionType> Type::GetFunctionType(std::shared_ptr<Type> ret,
|
||||
std::vector<std::shared_ptr<Type>> params) {
|
||||
// 提取裸指针用于键(保证唯一性,因为 shared_ptr 指向同一对象)
|
||||
std::vector<const Type*> param_ptrs;
|
||||
param_ptrs.reserve(params.size());
|
||||
for (const auto& p : params) {
|
||||
param_ptrs.push_back(p.get());
|
||||
}
|
||||
|
||||
FunctionKey key{ret.get(), std::move(param_ptrs)};
|
||||
auto& cache = GetFunctionCache();
|
||||
auto it = cache.find(key);
|
||||
if (it != cache.end()) {
|
||||
auto ptr = it->second.lock();
|
||||
if (ptr) return ptr;
|
||||
}
|
||||
|
||||
auto func = std::shared_ptr<FunctionType>(new FunctionType(std::move(ret), std::move(params)));
|
||||
cache[key] = func;
|
||||
return func;
|
||||
}
|
||||
|
||||
// ---------- ArrayType 实现 ----------
|
||||
ArrayType::ArrayType(std::shared_ptr<Type> elem, std::vector<int> dims)
|
||||
: Type(Kind::Array), elem_type_(std::move(elem)), dims_(std::move(dims)) {
|
||||
// 数组元素类型必须是完整类型
|
||||
assert(elem_type_ && elem_type_->IsComplete());
|
||||
}
|
||||
|
||||
size_t ArrayType::GetElementCount() const {
|
||||
size_t count = 1;
|
||||
for (int d : dims_) count *= d;
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t ArrayType::Size() const {
|
||||
return GetElementCount() * elem_type_->Size();
|
||||
}
|
||||
|
||||
size_t ArrayType::Alignment() const {
|
||||
// 数组对齐等于其元素对齐
|
||||
return elem_type_->Alignment();
|
||||
}
|
||||
|
||||
bool ArrayType::IsComplete() const {
|
||||
// 维度已确定且元素类型完整,则数组完整
|
||||
return !dims_.empty() && elem_type_->IsComplete();
|
||||
}
|
||||
|
||||
// ---------- FunctionType 实现 ----------
|
||||
FunctionType::FunctionType(std::shared_ptr<Type> ret,
|
||||
std::vector<std::shared_ptr<Type>> params)
|
||||
: Type(Kind::Function), return_type_(std::move(ret)), param_types_(std::move(params)) {}
|
||||
|
||||
size_t FunctionType::Size() const {
|
||||
// 函数类型没有运行时大小,通常用于类型检查,返回 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t FunctionType::Alignment() const {
|
||||
// 不对齐
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool FunctionType::IsComplete() const {
|
||||
// 函数类型总是完整的(只要返回类型完整,但 SysY 中 void 也视为完整)
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,162 @@
|
||||
// SysY 运行库实现:
|
||||
// - 按实验/评测规范提供 I/O 等函数实现
|
||||
// - 与编译器生成的目标代码链接,支撑运行时行为
|
||||
#include "sylib.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern int scanf(const char* format, ...);
|
||||
extern int printf(const char* format, ...);
|
||||
extern int getchar(void);
|
||||
extern int putchar(int c);
|
||||
|
||||
int getint(void) {
|
||||
int x = 0;
|
||||
scanf("%d", &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
int getch(void) {
|
||||
return getchar();
|
||||
}
|
||||
|
||||
int getarray(int a[]) {
|
||||
int n;
|
||||
scanf("%d", &n);
|
||||
int i = 0;
|
||||
for (; i < n; ++i) {
|
||||
scanf("%d", &a[i]);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
float getfloat(void) {
|
||||
float x = 0.0f;
|
||||
scanf("%f", &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
int getfarray(float a[]) {
|
||||
int n = 0;
|
||||
if (scanf("%d", &n) != 1) {
|
||||
return 0;
|
||||
}
|
||||
int i = 0;
|
||||
for (; i < n; ++i) {
|
||||
if (scanf("%f", &a[i]) != 1) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void putint(int x) {
|
||||
printf("%d", x);
|
||||
}
|
||||
|
||||
void putch(int x) {
|
||||
putchar(x);
|
||||
}
|
||||
|
||||
void putarray(int n, int a[]) {
|
||||
int i = 0;
|
||||
printf("%d:", n);
|
||||
for (; i < n; ++i) {
|
||||
printf(" %d", a[i]);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
void putfloat(float x) {
|
||||
printf("%a", x);
|
||||
}
|
||||
|
||||
void putfarray(int n, float a[]) {
|
||||
int i = 0;
|
||||
printf("%d:", n);
|
||||
for (; i < n; ++i) {
|
||||
printf(" %a", a[i]);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
void puts(int s[]) {
|
||||
if (!s) return;
|
||||
while (*s) {
|
||||
putchar(*s);
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
void _sysy_starttime(int lineno) {
|
||||
(void)lineno;
|
||||
}
|
||||
|
||||
void _sysy_stoptime(int lineno) {
|
||||
(void)lineno;
|
||||
}
|
||||
|
||||
void starttime(void) {
|
||||
_sysy_starttime(0);
|
||||
}
|
||||
|
||||
void stoptime(void) {
|
||||
_sysy_stoptime(0);
|
||||
}
|
||||
|
||||
int* memset(int* ptr, int value, int count) {
|
||||
unsigned char* p = (unsigned char*)ptr;
|
||||
unsigned char byte = (unsigned char)(value & 0xFF);
|
||||
int i = 0;
|
||||
for (; i < count; ++i) {
|
||||
p[i] = byte;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int* sysy_alloc_i32(int count) {
|
||||
if (count <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return (int*)malloc((size_t)count * sizeof(int));
|
||||
}
|
||||
|
||||
float* sysy_alloc_f32(int count) {
|
||||
if (count <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return (float*)malloc((size_t)count * sizeof(float));
|
||||
}
|
||||
|
||||
void sysy_free_i32(int* ptr) {
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void sysy_free_f32(float* ptr) {
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void sysy_zero_i32(int* ptr, int count) {
|
||||
int i = 0;
|
||||
if (!ptr || count <= 0) {
|
||||
return;
|
||||
}
|
||||
for (; i < count; ++i) {
|
||||
ptr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void sysy_zero_f32(float* ptr, int count) {
|
||||
int i = 0;
|
||||
if (!ptr || count <= 0) {
|
||||
return;
|
||||
}
|
||||
for (; i < count; ++i) {
|
||||
ptr[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,29 @@
|
||||
// SysY 运行库头文件:
|
||||
// - 声明运行库函数原型(供编译器生成 call 或链接阶段引用)
|
||||
// - 与 sylib.c 配套,按规范逐步补齐声明
|
||||
#pragma once
|
||||
|
||||
int getint(void);
|
||||
int getch(void);
|
||||
int getarray(int a[]);
|
||||
float getfloat(void);
|
||||
int getfarray(float a[]);
|
||||
|
||||
void putint(int x);
|
||||
void putch(int x);
|
||||
void putarray(int n, int a[]);
|
||||
void putfloat(float x);
|
||||
void putfarray(int n, float a[]);
|
||||
void puts(int s[]);
|
||||
|
||||
void _sysy_starttime(int lineno);
|
||||
void _sysy_stoptime(int lineno);
|
||||
void starttime(void);
|
||||
void stoptime(void);
|
||||
|
||||
int read_map(void);
|
||||
int* memset(int* ptr, int value, int count);
|
||||
int* sysy_alloc_i32(int count);
|
||||
float* sysy_alloc_f32(int count);
|
||||
void sysy_free_i32(int* ptr);
|
||||
void sysy_free_f32(float* ptr);
|
||||
void sysy_zero_i32(int* ptr, int count);
|
||||
void sysy_zero_f32(float* ptr, int count);
|
||||
|
||||
|
||||
Loading…
Reference in new issue