[midend] GEP类型推断函数getIndexedType逻辑修复,增加数组type缓存池避免相同type ==操作返回假,修复实参形参类型转换判断逻辑,starttime stoptime提供支持(待后端测试)

This commit is contained in:
rain2133
2025-07-25 03:26:10 +08:00
parent 96c6b0ab6e
commit 1e6f6ed711
3 changed files with 85 additions and 64 deletions

View File

@@ -105,8 +105,17 @@ FunctionType*FunctionType::get(Type *returnType, const std::vector<Type *> &para
} }
ArrayType *ArrayType::get(Type *elementType, unsigned numElements) { ArrayType *ArrayType::get(Type *elementType, unsigned numElements) {
// TODO:可以考虑在这里添加缓存,避免重复创建相同的数组类型 static std::set<std::unique_ptr<ArrayType>> arrayTypes;
return new ArrayType(elementType, numElements); auto iter = std::find_if(arrayTypes.begin(), arrayTypes.end(), [&](const std::unique_ptr<ArrayType> &type) -> bool {
return elementType == type->getElementType() && numElements == type->getNumElements();
});
if (iter != arrayTypes.end()) {
return iter->get();
}
auto type = new ArrayType(elementType, numElements);
assert(type);
auto result = arrayTypes.emplace(type);
return result.first->get();
} }
void Value::replaceAllUsesWith(Value *value) { void Value::replaceAllUsesWith(Value *value) {

View File

@@ -770,6 +770,7 @@ std::any SysYIRGenerator::visitLValue(SysYParser::LValueContext *ctx) {
if (allocatedType->isPointer()) { if (allocatedType->isPointer()) {
// 如果 AllocaInst 分配的是一个指针类型 (例如,用于存储函数参数的指针,如 int b[][20] 中的 b) // 如果 AllocaInst 分配的是一个指针类型 (例如,用于存储函数参数的指针,如 int b[][20] 中的 b)
// 即 `allocatedType` 是一个指向数组指针的指针 (e.g., [20 x i32]**)
// 那么 GEP 的基指针是加载这个指针变量的值。 // 那么 GEP 的基指针是加载这个指针变量的值。
gepBasePointer = builder.createLoadInst(alloc); // 加载出实际的指针值 (e.g., [20 x i32]*) gepBasePointer = builder.createLoadInst(alloc); // 加载出实际的指针值 (e.g., [20 x i32]*)
// 对于这种参数指针,用户提供的索引直接作用于它。不需要额外的 0。 // 对于这种参数指针,用户提供的索引直接作用于它。不需要额外的 0。
@@ -777,6 +778,7 @@ std::any SysYIRGenerator::visitLValue(SysYParser::LValueContext *ctx) {
} else { } else {
// 如果 AllocaInst 分配的是实际的数组数据 (例如int b[10][20] 中的 b) // 如果 AllocaInst 分配的是实际的数组数据 (例如int b[10][20] 中的 b)
// 那么 AllocaInst 本身就是 GEP 的基指针。 // 那么 AllocaInst 本身就是 GEP 的基指针。
// 这里的 `alloc` 是指向数组的指针 (e.g., [10 x [20 x i32]]*)
gepBasePointer = alloc; // 类型是 [10 x [20 x i32]]* gepBasePointer = alloc; // 类型是 [10 x [20 x i32]]*
// 对于这种完整的数组分配GEP 的第一个索引必须是 0用于“步过”整个数组。 // 对于这种完整的数组分配GEP 的第一个索引必须是 0用于“步过”整个数组。
gepIndices.push_back(ConstantInteger::get(0)); gepIndices.push_back(ConstantInteger::get(0));
@@ -856,32 +858,63 @@ std::any SysYIRGenerator::visitCall(SysYParser::CallContext *ctx) {
std::vector<Value *> args = {}; std::vector<Value *> args = {};
if (funcName == "starttime" || funcName == "stoptime") { if (funcName == "starttime" || funcName == "stoptime") {
// 如果是starttime或stoptime函数 args.emplace_back(
// TODO: 这里需要处理starttime和stoptime函数的参数 ConstantInteger::get(static_cast<int>(ctx->getStart()->getLine())));
// args.emplace_back()
} else { } else {
if (ctx->funcRParams() != nullptr) { if (ctx->funcRParams() != nullptr) {
args = std::any_cast<std::vector<Value *>>(visitFuncRParams(ctx->funcRParams())); args = std::any_cast<std::vector<Value *>>(visitFuncRParams(ctx->funcRParams()));
} }
auto params = function->getEntryBlock()->getArguments(); // 获取形参列表。`getArguments()` 返回的是 `Argument*` 的集合,
// 每个 `Argument` 代表一个函数形参,其 `getType()` 就是指向形参的类型的指针类型。
auto formalParamsAlloca = function->getEntryBlock()->getArguments();
// 检查实参和形参数量是否匹配。
if (args.size() != formalParamsAlloca.size()) {
std::cerr << "Error: Function call argument count mismatch for function '" << funcName << "'." << std::endl;
assert(false && "Function call argument count mismatch!");
}
for (int i = 0; i < args.size(); i++) { for (int i = 0; i < args.size(); i++) {
// 参数类型转换 // 形参的类型 (e.g., i32, float, i32*, [10 x i32]*)
if (params[i]->getType() != args[i]->getType() && Type* formalParamExpectedValueType = formalParamsAlloca[i]->getType()->as<PointerType>()->getBaseType();
(params[i]->getNumDims() != 0 || // 实参的实际类型 (e.g., i32, float, i32*, [67 x i32]*)
params[i]->getType()->as<PointerType>()->getBaseType() != args[i]->getType())) { Type* actualArgType = args[i]->getType();
ConstantValue * constValue = dynamic_cast<ConstantValue *>(args[i]); // 如果实参类型与形参类型不匹配,则尝试进行类型转换
if (formalParamExpectedValueType != actualArgType) {
ConstantValue *constValue = dynamic_cast<ConstantValue *>(args[i]);
if (constValue != nullptr) { if (constValue != nullptr) {
if (params[i]->getType() == Type::getPointerType(Type::getFloatType())) { if (formalParamExpectedValueType->isInt() && actualArgType->isFloat()) {
args[i] = ConstantInteger::get(static_cast<float>(constValue->getInt())); args[i] = ConstantInteger::get(static_cast<int>(constValue->getFloat()));
} else if (formalParamExpectedValueType->isFloat() && actualArgType->isInt()) {
args[i] = ConstantFloating::get(static_cast<float>(constValue->getInt()));
} else { } else {
args[i] = ConstantFloating::get(static_cast<int>(constValue->getFloat())); // 如果是常量但不是简单的 int/float 标量转换,
// 或者是指针常量需要 bitcast则让它进入非常量转换逻辑。
// 例如,一个常量数组的地址,需要 bitcast 成另一种指针类型。
// 目前不知道样例有没有这种情况,所以这里不做处理。
} }
} else { }
if (params[i]->getType() == Type::getPointerType(Type::getFloatType())) { else {
args[i] = builder.createIToFInst(args[i]); // 1. 标量值类型转换 (例如int_reg 到 float_regfloat_reg 到 int_reg)
} else { if (formalParamExpectedValueType->isInt() && actualArgType->isFloat()) {
args[i] = builder.createFtoIInst(args[i]); args[i] = builder.createFtoIInst(args[i]);
} else if (formalParamExpectedValueType->isFloat() && actualArgType->isInt()) {
args[i] = builder.createIToFInst(args[i]);
}
// 2. 指针类型转换 (例如数组退化:`[N x T]*` 到 `T*`,或兼容指针类型之间) TODO不清楚有没有这种样例
// 这种情况常见于数组参数,实参可能是一个更具体的数组指针类型,
// 而形参是其退化后的基础指针类型。LLVM 的 `bitcast` 指令可以用于
// 在相同大小的指针类型之间进行转换,这对于数组退化至关重要。
// else if (formalParamType->isPointer() && actualArgType->isPointer()) {
// 检查指针基类型是否兼容,或者是否是数组退化导致的类型不同。
// 使用 bitcast
// args[i] = builder.createBitCastInst(args[i], formalParamType);
// }
// 3. 其他未预期的类型不匹配
// 如果代码执行到这里,说明存在编译器前端未处理的类型不兼容或错误。
else {
// assert(false && "Unhandled type mismatch for function call argument.");
} }
} }
} }

View File

@@ -347,68 +347,47 @@ class IRBuilder {
static Type *getIndexedType(Type *pointerType, const std::vector<Value *> &indices) { static Type *getIndexedType(Type *pointerType, const std::vector<Value *> &indices) {
assert(pointerType->isPointer() && "base must be a pointer type!"); assert(pointerType->isPointer() && "base must be a pointer type!");
// Type *CurrentType = pointerType->as<PointerType>()->getBaseType();
// GEP 的类型推断从基指针所指向的类型开始。 // GEP 的类型推断从基指针所指向的类型开始。
// 例如: // 例如:
// - 如果 pointerType 是 `[20 x [10 x i32]]*` (指向一个二维数组的指针) // - 如果 pointerType 是 `[20 x [10 x i32]]*``currentWalkType` 初始为 `[20 x [10 x i32]]`。
// 那么 `currentWalkType` 将从 `[20 x [10 x i32]]` (二维数组类型) 开始 // - 如果 pointerType 是 `i32*``currentWalkType` 初始为 `i32`
// - 如果 pointerType 是 `i32*` (指向一个整数的指针) // - 如果 pointerType 是 `i32**``currentWalkType` 初始为 `i32*`。
// 那么 `currentWalkType` 将从 `i32` (整数类型) 开始。
Type *currentWalkType = pointerType->as<PointerType>()->getBaseType(); Type *currentWalkType = pointerType->as<PointerType>()->getBaseType();
// 遍历所有索引来深入类型层次结构。 // 遍历所有索引来深入类型层次结构。
// 注意:这里的 `indices` 向量通常已经包含了 `getGEPAddressInst` 添加的第一个“步过”索引通常是0 // `indices` 向量包含了所有 GEP 索引,包括由 `visitLValue` 等函数添加的初始 `0` 索引
// 因此,`indices[0]` 对应的是 GEP 操作的第一个逻辑步骤。
for (int i = 0; i < indices.size(); ++i) { for (int i = 0; i < indices.size(); ++i) {
if (currentWalkType->isArray()) { if (currentWalkType->isArray()) {
// 情况1:当前类型是数组类型 (例如 `[20 x [10 x i32]]` 或 `[10 x i32]`) // 情况:当前遍历类型是 `ArrayType`
// 此时,当前的索引用于选择数组中的一个元素(或子数组) // 索引用于选择数组元素,`currentWalkType` 更新为数组的元素类型
// 新的 `currentWalkType` 变为该数组的元素类型。
// 例如:`[20 x [10 x i32]]` 经过一个索引后,变为 `[10 x i32]`。
currentWalkType = currentWalkType->as<ArrayType>()->getElementType(); currentWalkType = currentWalkType->as<ArrayType>()->getElementType();
// } else if (currentWalkType->isStruct()) { } else if (currentWalkType->isPointer()) {
// // 情况2:当前类型是结构体类型 // 情况:当前遍历类型是 `PointerType`
// // 此时,索引必须是一个常量整数,用于选择结构体中的特定成员 // 这意味着我们正在通过一个指针来访问其指向的内存
// // SysY 语言通常只支持数组,但如果你的 IR 支持结构体,这里需要实现 // 索引用于选择该指针所指向的“数组”的元素
// ConstantInteger* structIdx = dynamic_cast<ConstantInteger*>(indices[i]); // `currentWalkType` 更新为该指针所指向的基础类型。
// assert(structIdx && "Struct index must be a constant integer!"); // 例如:如果 `currentWalkType` 是 `i32*`,它将变为 `i32`。
// assert(structIdx->getInt() >= 0 && "Struct index cannot be negative!"); // 如果 `currentWalkType` 是 `[10 x i32]*`,它将变为 `[10 x i32]`。
// // 确保 `StructType` 类有 `getNumMembers()` 和 `getMemberType()` 方法。 currentWalkType = currentWalkType->as<PointerType>()->getBaseType();
// // 如果你的 Type 系统没有这些方法,需要根据实际情况调整。
// assert(structIdx->getInt() < currentWalkType->as<StructType>()->getNumMembers() && "Struct index out of bounds!");
// currentWalkType = currentWalkType->as<StructType>()->getMemberType(structIdx->getInt());
} else { } else {
// 情况3:当前类型既不是数组也不是结构体(即它是一个标量类型如 `i32` `float` // 情况:当前遍历类型是标量类型 (例如 `i32`, `float` 等非聚合、非指针类型)
// //
// 如果 `currentWalkType` 是一个标量类型,并且**后面还有未处理的索引** (`i < indices.size() - 1`) // 如果 `currentWalkType` 是标量,并且当前索引 `i` **不是** `indices` 向量中的最后一个索引
// 这意味着我们试图对一个标量类型进行进一步的结构性索引,这是**无效的**。 // 这意味着尝试对一个标量类型进行进一步的结构性索引,这是**无效的**。
// 例如:`int* ptr; ptr[0][0];` // 例如:`int x; x[0];` 对应的 GEP 链中,`x` 的类型是 `i32`,再加 `[0]` 索引就是错误。
// - `ptr` 的类型是 `int*`。
// - `currentWalkType` 初始化为 `int`。
// - `i = 0` 时,`currentWalkType` 是 `int`。它不是数组/结构体,类型不变。
// - `i = 1` 时,`currentWalkType` 仍然是 `int`。此时 `i < indices.size() - 1` (即 `1 < 2 - 1 = 1`) 为假。
// 因为 `indices.size()` 是 2 (`[0, 0]`)`i` 是 1。`i < indices.size() - 1` 是 `1 < 1`,为假。
// 所以不会触发断言,`currentWalkType` 保持 `int`。这是正确的。
// //
// 让我重新检查一下 `if (i > 0)` 和 `if (i < indices.size() - 1)` 的区别。 // 如果 `currentWalkType` 是标量,且这是**最后一个索引** (`i == indices.size() - 1`)
// 原始的 `if (i > 0)` 导致 `arr[1]` 失败是因为 `currentWalkType` 变成 `int` 后, // 那么 GEP 是合法的,它只是计算一个偏移地址,最终的类型就是这个标量类型。
// `i=1` 触发了断言 // 此时 `currentWalkType` 保持不变,循环结束
//
// LLVM GEP 的行为是:如果当前类型是标量,并且这是 GEP 的**最后一个索引**,那么 GEP 是合法的,
// 最终的类型就是这个标量类型。如果不是最后一个索引,则报错。
// 修正后的判断:
// 如果当前类型是标量,并且当前索引 `i` 不是 `indices` 向量中的**最后一个索引**
// 那么就意味着尝试对标量进行额外的结构性索引,这是错误的。
if (i < indices.size() - 1) { if (i < indices.size() - 1) {
assert(false && "Invalid GEP indexing: attempting to index into a non-aggregate type with further indices."); assert(false && "Invalid GEP indexing: attempting to index into a non-aggregate/non-pointer type with further indices.");
return nullptr; // 返回空指针表示类型推断失败 return nullptr; // 返回空指针表示类型推断失败
} }
// 如果 `currentWalkType` 是标量,并且这是最后一个索引,则类型保持不变。 // 如果是最后一个索引,且当前类型是标量,则类型保持不变,这是合法的
// 这是合法的 GEP 操作,例如 `getelementptr i32, i32* %ptr, i64 5`。 // 循环会自然结束,返回正确的 `currentWalkType`。
// `currentWalkType` 将是 `i32`,并且循环会在此结束。
} }
} }
// 所有索引处理完毕后,`currentWalkType` 就是 GEP 指令最终计算出的地址所指向的元素的类型。
return currentWalkType; return currentWalkType;
} }
}; };