Skip to content

Commit 89fb849

Browse files
llvm-beanzdamyanprjmccall
authored
[HLSL] Implement output parameter (#101083)
HLSL output parameters are denoted with the `inout` and `out` keywords in the function declaration. When an argument to an output parameter is constructed a temporary value is constructed for the argument. For `inout` pamameters the argument is initialized via copy-initialization from the argument lvalue expression to the parameter type. For `out` parameters the argument is not initialized before the call. In both cases on return of the function the temporary value is written back to the argument lvalue expression through an implicit assignment binary operator with casting as required. This change introduces a new HLSLOutArgExpr ast node which represents the output argument behavior. The OutArgExpr has three defined children: - An OpaqueValueExpr of the argument lvalue expression. - An OpaqueValueExpr of the copy-initialized parameter. - A BinaryOpExpr assigning the first with the value of the second. Fixes #87526 --------- Co-authored-by: Damyan Pepper <[email protected]> Co-authored-by: John McCall <[email protected]>
1 parent e41579a commit 89fb849

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1220
-96
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,6 +1381,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
13811381
/// in the return type and parameter types.
13821382
bool hasSameFunctionTypeIgnoringPtrSizes(QualType T, QualType U);
13831383

1384+
/// Get or construct a function type that is equivalent to the input type
1385+
/// except that the parameter ABI annotations are stripped.
1386+
QualType getFunctionTypeWithoutParamABIs(QualType T) const;
1387+
1388+
/// Determine if two function types are the same, ignoring parameter ABI
1389+
/// annotations.
1390+
bool hasSameFunctionTypeIgnoringParamABI(QualType T, QualType U) const;
1391+
13841392
/// Return the uniqued reference to the type for a complex
13851393
/// number with the specified element type.
13861394
QualType getComplexType(QualType T) const;

clang/include/clang/AST/Attr.h

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -224,20 +224,7 @@ class ParameterABIAttr : public InheritableParamAttr {
224224
InheritEvenIfAlreadyPresent) {}
225225

226226
public:
227-
ParameterABI getABI() const {
228-
switch (getKind()) {
229-
case attr::SwiftContext:
230-
return ParameterABI::SwiftContext;
231-
case attr::SwiftAsyncContext:
232-
return ParameterABI::SwiftAsyncContext;
233-
case attr::SwiftErrorResult:
234-
return ParameterABI::SwiftErrorResult;
235-
case attr::SwiftIndirectResult:
236-
return ParameterABI::SwiftIndirectResult;
237-
default:
238-
llvm_unreachable("bad parameter ABI attribute kind");
239-
}
240-
}
227+
ParameterABI getABI() const;
241228

242229
static bool classof(const Attr *A) {
243230
return A->getKind() >= attr::FirstParameterABIAttr &&
@@ -379,6 +366,29 @@ inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
379366
DB.AddTaggedVal(reinterpret_cast<uint64_t>(At), DiagnosticsEngine::ak_attr);
380367
return DB;
381368
}
369+
370+
inline ParameterABI ParameterABIAttr::getABI() const {
371+
switch (getKind()) {
372+
case attr::SwiftContext:
373+
return ParameterABI::SwiftContext;
374+
case attr::SwiftAsyncContext:
375+
return ParameterABI::SwiftAsyncContext;
376+
case attr::SwiftErrorResult:
377+
return ParameterABI::SwiftErrorResult;
378+
case attr::SwiftIndirectResult:
379+
return ParameterABI::SwiftIndirectResult;
380+
case attr::HLSLParamModifier: {
381+
const auto *A = cast<HLSLParamModifierAttr>(this);
382+
if (A->isOut())
383+
return ParameterABI::HLSLOut;
384+
if (A->isInOut())
385+
return ParameterABI::HLSLInOut;
386+
return ParameterABI::Ordinary;
387+
}
388+
default:
389+
llvm_unreachable("bad parameter ABI attribute kind");
390+
}
391+
}
382392
} // end namespace clang
383393

384394
#endif

clang/include/clang/AST/Expr.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7071,6 +7071,103 @@ class ArraySectionExpr : public Expr {
70717071
void setRBracketLoc(SourceLocation L) { RBracketLoc = L; }
70727072
};
70737073

7074+
/// This class represents temporary values used to represent inout and out
7075+
/// arguments in HLSL. From the callee perspective these parameters are more or
7076+
/// less __restrict__ T&. They are guaranteed to not alias any memory. inout
7077+
/// parameters are initialized by the caller, and out parameters are references
7078+
/// to uninitialized memory.
7079+
///
7080+
/// In the caller, the argument expression creates a temporary in local memory
7081+
/// and the address of the temporary is passed into the callee. There may be
7082+
/// implicit conversion sequences to initialize the temporary, and on expiration
7083+
/// of the temporary an inverse conversion sequence is applied as a write-back
7084+
/// conversion to the source l-value.
7085+
///
7086+
/// This AST node has three sub-expressions:
7087+
/// - An OpaqueValueExpr with a source that is the argument lvalue expression.
7088+
/// - An OpaqueValueExpr with a source that is an implicit conversion
7089+
/// sequence from the source lvalue to the argument type.
7090+
/// - An expression that assigns the second expression into the first,
7091+
/// performing any necessary conversions.
7092+
class HLSLOutArgExpr : public Expr {
7093+
friend class ASTStmtReader;
7094+
7095+
enum {
7096+
BaseLValue,
7097+
CastedTemporary,
7098+
WritebackCast,
7099+
NumSubExprs,
7100+
};
7101+
7102+
Stmt *SubExprs[NumSubExprs];
7103+
bool IsInOut;
7104+
7105+
HLSLOutArgExpr(QualType Ty, OpaqueValueExpr *B, OpaqueValueExpr *OpV,
7106+
Expr *WB, bool IsInOut)
7107+
: Expr(HLSLOutArgExprClass, Ty, VK_LValue, OK_Ordinary),
7108+
IsInOut(IsInOut) {
7109+
SubExprs[BaseLValue] = B;
7110+
SubExprs[CastedTemporary] = OpV;
7111+
SubExprs[WritebackCast] = WB;
7112+
assert(!Ty->isDependentType() && "HLSLOutArgExpr given a dependent type!");
7113+
}
7114+
7115+
explicit HLSLOutArgExpr(EmptyShell Shell)
7116+
: Expr(HLSLOutArgExprClass, Shell) {}
7117+
7118+
public:
7119+
static HLSLOutArgExpr *Create(const ASTContext &C, QualType Ty,
7120+
OpaqueValueExpr *Base, OpaqueValueExpr *OpV,
7121+
Expr *WB, bool IsInOut);
7122+
static HLSLOutArgExpr *CreateEmpty(const ASTContext &Ctx);
7123+
7124+
const OpaqueValueExpr *getOpaqueArgLValue() const {
7125+
return cast<OpaqueValueExpr>(SubExprs[BaseLValue]);
7126+
}
7127+
OpaqueValueExpr *getOpaqueArgLValue() {
7128+
return cast<OpaqueValueExpr>(SubExprs[BaseLValue]);
7129+
}
7130+
7131+
/// Return the l-value expression that was written as the argument
7132+
/// in source. Everything else here is implicitly generated.
7133+
const Expr *getArgLValue() const {
7134+
return getOpaqueArgLValue()->getSourceExpr();
7135+
}
7136+
Expr *getArgLValue() { return getOpaqueArgLValue()->getSourceExpr(); }
7137+
7138+
const Expr *getWritebackCast() const {
7139+
return cast<Expr>(SubExprs[WritebackCast]);
7140+
}
7141+
Expr *getWritebackCast() { return cast<Expr>(SubExprs[WritebackCast]); }
7142+
7143+
const OpaqueValueExpr *getCastedTemporary() const {
7144+
return cast<OpaqueValueExpr>(SubExprs[CastedTemporary]);
7145+
}
7146+
OpaqueValueExpr *getCastedTemporary() {
7147+
return cast<OpaqueValueExpr>(SubExprs[CastedTemporary]);
7148+
}
7149+
7150+
/// returns true if the parameter is inout and false if the parameter is out.
7151+
bool isInOut() const { return IsInOut; }
7152+
7153+
SourceLocation getBeginLoc() const LLVM_READONLY {
7154+
return SubExprs[BaseLValue]->getBeginLoc();
7155+
}
7156+
7157+
SourceLocation getEndLoc() const LLVM_READONLY {
7158+
return SubExprs[BaseLValue]->getEndLoc();
7159+
}
7160+
7161+
static bool classof(const Stmt *T) {
7162+
return T->getStmtClass() == HLSLOutArgExprClass;
7163+
}
7164+
7165+
// Iterators
7166+
child_range children() {
7167+
return child_range(&SubExprs[BaseLValue], &SubExprs[NumSubExprs]);
7168+
}
7169+
};
7170+
70747171
/// Frontend produces RecoveryExprs on semantic errors that prevent creating
70757172
/// other well-formed expressions. E.g. when type-checking of a binary operator
70767173
/// fails, we cannot produce a BinaryOperator expression. Instead, we can choose

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4055,6 +4055,9 @@ DEF_TRAVERSE_STMT(OpenACCComputeConstruct,
40554055
DEF_TRAVERSE_STMT(OpenACCLoopConstruct,
40564056
{ TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
40574057

4058+
// Traverse HLSL: Out argument expression
4059+
DEF_TRAVERSE_STMT(HLSLOutArgExpr, {})
4060+
40584061
// FIXME: look at the following tricky-seeming exprs to see if we
40594062
// need to recurse on anything. These are ones that have methods
40604063
// returning decls or qualtypes or nestednamespecifier -- though I'm

clang/include/clang/AST/TextNodeDumper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ class TextNodeDumper
407407
void
408408
VisitLifetimeExtendedTemporaryDecl(const LifetimeExtendedTemporaryDecl *D);
409409
void VisitHLSLBufferDecl(const HLSLBufferDecl *D);
410+
void VisitHLSLOutArgExpr(const HLSLOutArgExpr *E);
410411
void VisitOpenACCConstructStmt(const OpenACCConstructStmt *S);
411412
void VisitOpenACCLoopConstruct(const OpenACCLoopConstruct *S);
412413
void VisitEmbedExpr(const EmbedExpr *S);

clang/include/clang/Basic/Attr.td

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4639,14 +4639,13 @@ def HLSLGroupSharedAddressSpace : TypeAttr {
46394639
let Documentation = [HLSLGroupSharedAddressSpaceDocs];
46404640
}
46414641

4642-
def HLSLParamModifier : TypeAttr {
4642+
def HLSLParamModifier : ParameterABIAttr {
46434643
let Spellings = [CustomKeyword<"in">, CustomKeyword<"inout">, CustomKeyword<"out">];
46444644
let Accessors = [Accessor<"isIn", [CustomKeyword<"in">]>,
46454645
Accessor<"isInOut", [CustomKeyword<"inout">]>,
46464646
Accessor<"isOut", [CustomKeyword<"out">]>,
46474647
Accessor<"isAnyOut", [CustomKeyword<"out">, CustomKeyword<"inout">]>,
46484648
Accessor<"isAnyIn", [CustomKeyword<"in">, CustomKeyword<"inout">]>];
4649-
let Subjects = SubjectList<[ParmVar]>;
46504649
let Documentation = [HLSLParamQualifierDocs];
46514650
let Args = [DefaultBoolArgument<"MergedSpelling", /*default*/0, /*fake*/1>];
46524651
}

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12380,6 +12380,8 @@ def warn_hlsl_availability : Warning<
1238012380
def warn_hlsl_availability_unavailable :
1238112381
Warning<err_unavailable.Summary>,
1238212382
InGroup<HLSLAvailability>, DefaultError;
12383+
def error_hlsl_inout_scalar_extension : Error<"illegal scalar extension cast on argument %0 to %select{|in}1out paramemter">;
12384+
def error_hlsl_inout_lvalue : Error<"cannot bind non-lvalue argument %0 to %select{|in}1out paramemter">;
1238312385

1238412386
def err_hlsl_export_not_on_function : Error<
1238512387
"export declaration can only be used on functions">;

clang/include/clang/Basic/Specifiers.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,12 @@ namespace clang {
385385
/// Swift asynchronous context-pointer ABI treatment. There can be at
386386
/// most one parameter on a given function that uses this treatment.
387387
SwiftAsyncContext,
388+
389+
// This parameter is a copy-out HLSL parameter.
390+
HLSLOut,
391+
392+
// This parameter is a copy-in/copy-out HLSL parameter.
393+
HLSLInOut,
388394
};
389395

390396
/// Assigned inheritance model for a class in the MS C++ ABI. Must match order

clang/include/clang/Basic/StmtNodes.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,6 @@ def OpenACCAssociatedStmtConstruct
307307
: StmtNode<OpenACCConstructStmt, /*abstract=*/1>;
308308
def OpenACCComputeConstruct : StmtNode<OpenACCAssociatedStmtConstruct>;
309309
def OpenACCLoopConstruct : StmtNode<OpenACCAssociatedStmtConstruct>;
310+
311+
// HLSL Constructs.
312+
def HLSLOutArgExpr : StmtNode<Expr>;

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ class SemaHLSL : public SemaBase {
7171

7272
// HLSL Type trait implementations
7373
bool IsScalarizedLayoutCompatible(QualType T1, QualType T2) const;
74+
75+
bool CheckCompatibleParameterABI(FunctionDecl *New, FunctionDecl *Old);
76+
77+
ExprResult ActOnOutParamExpr(ParmVarDecl *Param, Expr *Arg);
78+
79+
QualType getInoutParameterType(QualType Ty);
7480
};
7581

7682
} // namespace clang

0 commit comments

Comments
 (0)