diff options
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 47 | ||||
| -rw-r--r-- | source/slang/slang-ir-util.cpp | 65 | ||||
| -rw-r--r-- | tests/optimization/func-resource-result/init-copy-eliminate.slang | 40 |
3 files changed, 94 insertions, 58 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index fa31c54bd..4bfbde584 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -614,7 +614,6 @@ private: Decl* member, Expr* rightHandSideExpr); Expr* createCtorParamExpr(ConstructorDecl* ctor, Index paramIndex); - void maybeInsertDefaultInitExpr(StructDecl* structDecl); }; template<typename VisitorType> @@ -9918,51 +9917,6 @@ void SemanticsDeclBodyVisitor::synthesizeCtorBodyForMemberVar( seqStmtChild->stmts.add(stmt); } -// This function inserts the default initialization expression for every member who -// has init expression at beginning of the user-defined ctor. We don't need to do this -// for synthesized ctor, because synthesized ctor already handle this at the function -// parameters -void SemanticsDeclBodyVisitor::maybeInsertDefaultInitExpr(StructDecl* structDecl) -{ - // If there is no explicit constructor, we don't need to do anything. - if (!_hasExplicitConstructor(structDecl, false)) - return; - - // traverse all the constructors and insert the default initialization expressions. - for (auto ctor : structDecl->getMembersOfType<ConstructorDecl>()) - { - ThisExpr* thisExpr = m_astBuilder->create<ThisExpr>(); - thisExpr->scope = ctor->ownedScope; - thisExpr->type = ctor->returnType.type; - auto seqStmtChild = m_astBuilder->create<SeqStmt>(); - - for (auto varDeclBase : structDecl->getDirectMemberDeclsOfType<VarDeclBase>()) - { - if (varDeclBase->hasModifier<HLSLStaticModifier>() || - varDeclBase->getName() == nullptr || varDeclBase->initExpr == nullptr) - continue; - - auto assign = createMemberAssignmentExpr( - thisExpr, - ctor->ownedScope, - varDeclBase, - varDeclBase->initExpr); - if (!assign) - continue; - - auto stmt = m_astBuilder->create<ExpressionStmt>(); - stmt->expression = assign; - stmt->loc = varDeclBase->loc; - seqStmtChild->stmts.add(stmt); - } - if (seqStmtChild->stmts.getCount() != 0) - { - auto seqStmt = _ensureCtorBodyIsSeqStmt(m_astBuilder, ctor); - seqStmt->stmts.insert(0, seqStmtChild); - } - } -} - void SemanticsDeclBodyVisitor::synthesizeCtorBody( DeclAndCtorInfo& structDeclInfo, List<DeclAndCtorInfo>& inheritanceDefaultCtorList, @@ -10063,7 +10017,6 @@ void SemanticsDeclBodyVisitor::visitAggTypeDecl(AggTypeDecl* aggTypeDecl) } synthesizeCtorBody(structDeclInfo, inheritanceDefaultCtorList, structDecl); - maybeInsertDefaultInitExpr(structDecl); if (structDeclInfo.defaultCtor) { diff --git a/source/slang/slang-ir-util.cpp b/source/slang/slang-ir-util.cpp index 9bec464dc..5745ef85f 100644 --- a/source/slang/slang-ir-util.cpp +++ b/source/slang/slang-ir-util.cpp @@ -800,6 +800,7 @@ IRInst* getRootAddr(IRInst* addr, List<IRInst*>& outAccessChain, List<IRInst*>* return addr; } + IRInst* getRootBufferOrAddr(IRInst* addr) { auto rootAddr = getRootAddr(addr); @@ -953,11 +954,11 @@ bool canAddressesPotentiallyAlias( if (addr1 == addr2) return true; - addr1 = getRootBufferOrAddr(addr1); - addr2 = getRootBufferOrAddr(addr2); + auto root1 = getRootBufferOrAddr(addr1); + auto root2 = getRootBufferOrAddr(addr2); - auto addr1Class = getAliasingClass(addr1); - auto addr2Class = getAliasingClass(addr2); + auto addr1Class = getAliasingClass(root1); + auto addr2Class = getAliasingClass(root2); if (!canAddrClassesAlias(addr1Class, addr2Class)) return false; @@ -974,17 +975,17 @@ bool canAddressesPotentiallyAlias( case AddressAliasingClass::BoundBuffer: case AddressAliasingClass::BoundTexture: case AddressAliasingClass::ConstantBuffer: - if (addr1 != addr2) + if (root1 != root2) return false; break; } } // A param and a var can never alias. - if (addr1->getOp() == kIROp_Param && addr1->getParent() == func->getFirstBlock() && - addr2->getOp() == kIROp_Var || - addr1->getOp() == kIROp_Var && addr2->getOp() == kIROp_Param && - addr2->getParent() == func->getFirstBlock()) + if (root1->getOp() == kIROp_Param && root1->getParent() == func->getFirstBlock() && + root2->getOp() == kIROp_Var || + root1->getOp() == kIROp_Var && root2->getOp() == kIROp_Param && + root2->getParent() == func->getFirstBlock()) return false; // If one addr is user pointer and one addr is a var, @@ -992,11 +993,53 @@ bool canAddressesPotentiallyAlias( // the var. if (addr1Class == AddressAliasingClass::Var && addr2Class == AddressAliasingClass::UserPointer) { - return canVarAliasWithUserPointer(target, addr1); + return canVarAliasWithUserPointer(target, root1); } if (addr2Class == AddressAliasingClass::Var && addr1Class == AddressAliasingClass::UserPointer) { - return canVarAliasWithUserPointer(target, addr2); + return canVarAliasWithUserPointer(target, root2); + } + + // If two addrs are rooted from the same object but found to statically differ in access chain, + // then they cannot alias. + if (root1 == root2) + { + List<IRInst*> accessChain1; + List<IRInst*> accessChain2; + + // Since getRootBufferOrAddr has a different behavior around + // RWStructuredBufferGetElementPtr compared to getRootAddr, + // we need to call getRootAddr here again to get a simpler access chain + // that we can handle here, so that we don't need to handle the nuance + // of whether or not to trace past any RWStructuredBufferGetElementPtr. + // + root1 = getRootAddr(addr1, accessChain1, nullptr); + root2 = getRootAddr(addr2, accessChain2, nullptr); + if (root1 != root2) + return true; + for (Index i = 0; i < Math::Min(accessChain1.getCount(), accessChain2.getCount()); i++) + { + auto node1 = accessChain1[i]; + auto node2 = accessChain2[i]; + if (as<IRStructKey>(node1) && as<IRStructKey>(node2)) + { + // Two different field keys means the two addresses cannot alias. + // TODO: If we are going to support union types, we need to exclude that + // here. + if (node1 != node2) + return false; + // If the keys are the same, continue looking further down the access chain. + continue; + } + // Two different constant indices means the two addresses cannot alias. + auto index1 = as<IRIntLit>(node1); + auto index2 = as<IRIntLit>(node2); + if (index1 && index2 && index1->getValue() != index2->getValue()) + return false; + // In all other cases, such as when either one of the indices is + // a untime value, we treat the two indices as potentially being the same. + return true; + } } return true; } diff --git a/tests/optimization/func-resource-result/init-copy-eliminate.slang b/tests/optimization/func-resource-result/init-copy-eliminate.slang new file mode 100644 index 000000000..c7c8eadcf --- /dev/null +++ b/tests/optimization/func-resource-result/init-copy-eliminate.slang @@ -0,0 +1,40 @@ +//TEST:SIMPLE(filecheck=CHECK):-target cuda -entry computeMain -stage compute + +// Test on a regression that caused a field to be initialized twice in the ctor. + +// CHECK-COUNT-1: callOnce{{.*}}(int(1000)) +// CHECK-COUNT-1: {{.*}}->origin_0 = +int callOnce(int x) +{ + *result += 1.0f; // cause some side effect so the call is not eliminated + return 999; +} + +public struct Ray +{ + public int calledOnce = callOnce(1000); + public float3 origin = {}; + public float t_min = 0; + public float3 dir = {}; + public float t_max = 0; + + public __init(float3 origin, float3 dir, float t_min = 0.f, float t_max = 1000.0) + { + this.origin = origin; + this.dir = dir; + this.t_min = t_min; + this.t_max = t_max; + } + + public RayDesc to_ray_desc() { return { origin, t_min, dir, t_max }; } +}; + +uniform float* result; + +[numthreads(1,1,1)] +void computeMain() +{ + Ray r = Ray(float3(1,2,3), float3(4,5,6)); + RayDesc rd = r.to_ray_desc(); + *result = rd.Direction.x; +} |
