Skip to content

Commit 66eccfb

Browse files
jrtc27resistor
authored andcommitted
[Sema][CodeGen] Support __builtin_<op>_overflow with __intcap
Morello LLVM has downstream support for this, but it's both incomplete (see https://git.morello-project.org/morello/llvm-project/-/issues/80) and incorrect with regards to provenance (in that it takes a naive type-based approach rather than considering the cheri_no_provenance attribute, meaning it differs from the binary operators in provenance semantics). This is a from-scratch implementation that aims to not have the same shortcomings. Fixes CTSRD-CHERI#720
1 parent 32b7356 commit 66eccfb

File tree

4 files changed

+5668
-15
lines changed

4 files changed

+5668
-15
lines changed

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 121 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#include "clang/Basic/TargetInfo.h"
3030
#include "clang/Frontend/FrontendDiagnostic.h"
3131
#include "llvm/Analysis/AssumptionCache.h"
32+
#include "llvm/Analysis/ValueTracking.h"
33+
#include "llvm/IR/Constants.h"
34+
#include "llvm/IR/DataLayout.h"
3235
#include "llvm/IR/InlineAsm.h"
3336
#include "llvm/IR/Instruction.h"
3437
#include "llvm/IR/IntrinsicInst.h"
@@ -2321,14 +2324,40 @@ static RValue EmitCheckedUnsignedMultiplySignedResult(
23212324
CodeGenFunction &CGF, const clang::Expr *Op1, WidthAndSignedness Op1Info,
23222325
const clang::Expr *Op2, WidthAndSignedness Op2Info,
23232326
const clang::Expr *ResultArg, QualType ResultQTy,
2324-
WidthAndSignedness ResultInfo) {
2327+
WidthAndSignedness ResultInfo, SourceLocation Loc) {
23252328
assert(isSpecialUnsignedMultiplySignedResult(
23262329
Builtin::BI__builtin_mul_overflow, Op1Info, Op2Info, ResultInfo) &&
23272330
"Cannot specialize this multiply");
23282331

2332+
clang::QualType Op1QTy = Op1->getType();
2333+
clang::QualType Op2QTy = Op2->getType();
2334+
bool Op1IsCap = Op1QTy->isCHERICapabilityType(CGF.getContext());
2335+
bool Op2IsCap = Op2QTy->isCHERICapabilityType(CGF.getContext());
2336+
bool ResultIsCap = ResultQTy->isCHERICapabilityType(CGF.getContext());
2337+
23292338
llvm::Value *V1 = CGF.EmitScalarExpr(Op1);
23302339
llvm::Value *V2 = CGF.EmitScalarExpr(Op2);
23312340

2341+
llvm::Value *ProvenanceCap = nullptr;
2342+
if (ResultIsCap) {
2343+
bool Op1NoProvenance =
2344+
!Op1IsCap || Op1QTy->hasAttr(attr::CHERINoProvenance);
2345+
bool Op2NoProvenance =
2346+
!Op2IsCap || Op2QTy->hasAttr(attr::CHERINoProvenance);
2347+
if (Op1NoProvenance && Op2NoProvenance)
2348+
ProvenanceCap = llvm::ConstantPointerNull::get(CGF.Int8CheriCapTy);
2349+
else if (Op1NoProvenance)
2350+
ProvenanceCap = V2;
2351+
else
2352+
ProvenanceCap = V1;
2353+
}
2354+
2355+
if (Op1IsCap)
2356+
V1 = CGF.getCapabilityIntegerValue(V1);
2357+
2358+
if (Op2IsCap)
2359+
V2 = CGF.getCapabilityIntegerValue(V2);
2360+
23322361
llvm::Value *HasOverflow;
23332362
llvm::Value *Result = EmitOverflowIntrinsic(
23342363
CGF, Intrinsic::umul_with_overflow, V1, V2, HasOverflow);
@@ -2342,6 +2371,9 @@ static RValue EmitCheckedUnsignedMultiplySignedResult(
23422371
llvm::Value *IntMaxOverflow = CGF.Builder.CreateICmpUGT(Result, IntMaxValue);
23432372
HasOverflow = CGF.Builder.CreateOr(HasOverflow, IntMaxOverflow);
23442373

2374+
if (ResultIsCap)
2375+
Result = CGF.setCapabilityIntegerValue(ProvenanceCap, Result, Loc);
2376+
23452377
bool isVolatile =
23462378
ResultArg->getType()->getPointeeType().isVolatileQualified();
23472379
Address ResultPtr = CGF.EmitPointerWithAlignment(ResultArg);
@@ -2362,23 +2394,50 @@ static bool isSpecialMixedSignMultiply(unsigned BuiltinID,
23622394

23632395
/// Emit a checked mixed-sign multiply. This is a cheaper specialization of
23642396
/// the generic checked-binop irgen.
2365-
static RValue
2366-
EmitCheckedMixedSignMultiply(CodeGenFunction &CGF, const clang::Expr *Op1,
2367-
WidthAndSignedness Op1Info, const clang::Expr *Op2,
2368-
WidthAndSignedness Op2Info,
2369-
const clang::Expr *ResultArg, QualType ResultQTy,
2370-
WidthAndSignedness ResultInfo) {
2397+
static RValue EmitCheckedMixedSignMultiply(
2398+
CodeGenFunction &CGF, const clang::Expr *Op1, WidthAndSignedness Op1Info,
2399+
const clang::Expr *Op2, WidthAndSignedness Op2Info,
2400+
const clang::Expr *ResultArg, QualType ResultQTy,
2401+
WidthAndSignedness ResultInfo, SourceLocation Loc) {
23712402
assert(isSpecialMixedSignMultiply(Builtin::BI__builtin_mul_overflow, Op1Info,
23722403
Op2Info, ResultInfo) &&
23732404
"Not a mixed-sign multipliction we can specialize");
23742405

2406+
QualType Op1QTy = Op1->getType();
2407+
QualType Op2QTy = Op2->getType();
2408+
bool Op1IsCap = Op1QTy->isCHERICapabilityType(CGF.getContext());
2409+
bool Op2IsCap = Op2QTy->isCHERICapabilityType(CGF.getContext());
2410+
bool ResultIsCap = ResultQTy->isCHERICapabilityType(CGF.getContext());
2411+
23752412
// Emit the signed and unsigned operands.
23762413
const clang::Expr *SignedOp = Op1Info.Signed ? Op1 : Op2;
23772414
const clang::Expr *UnsignedOp = Op1Info.Signed ? Op2 : Op1;
23782415
llvm::Value *Signed = CGF.EmitScalarExpr(SignedOp);
23792416
llvm::Value *Unsigned = CGF.EmitScalarExpr(UnsignedOp);
23802417
unsigned SignedOpWidth = Op1Info.Signed ? Op1Info.Width : Op2Info.Width;
23812418
unsigned UnsignedOpWidth = Op1Info.Signed ? Op2Info.Width : Op1Info.Width;
2419+
bool SignedIsCap = Op1Info.Signed ? Op1IsCap : Op2IsCap;
2420+
bool UnsignedIsCap = Op1Info.Signed ? Op2IsCap : Op1IsCap;
2421+
2422+
llvm::Value *ProvenanceCap = nullptr;
2423+
if (ResultIsCap) {
2424+
bool Op1NoProvenance =
2425+
!Op1IsCap || Op1QTy->hasAttr(attr::CHERINoProvenance);
2426+
bool Op2NoProvenance =
2427+
!Op2IsCap || Op2QTy->hasAttr(attr::CHERINoProvenance);
2428+
if (Op1NoProvenance && Op2NoProvenance)
2429+
ProvenanceCap = llvm::ConstantPointerNull::get(CGF.Int8CheriCapTy);
2430+
else if (Op1NoProvenance)
2431+
ProvenanceCap = Op1Info.Signed ? Unsigned : Signed;
2432+
else
2433+
ProvenanceCap = Op1Info.Signed ? Signed : Unsigned;
2434+
}
2435+
2436+
if (SignedIsCap)
2437+
Signed = CGF.getCapabilityIntegerValue(Signed);
2438+
2439+
if (UnsignedIsCap)
2440+
Unsigned = CGF.getCapabilityIntegerValue(Unsigned);
23822441

23832442
// One of the operands may be smaller than the other. If so, [s|z]ext it.
23842443
if (SignedOpWidth < UnsignedOpWidth)
@@ -2389,7 +2448,9 @@ EmitCheckedMixedSignMultiply(CodeGenFunction &CGF, const clang::Expr *Op1,
23892448
llvm::Type *OpTy = Signed->getType();
23902449
llvm::Value *Zero = llvm::Constant::getNullValue(OpTy);
23912450
Address ResultPtr = CGF.EmitPointerWithAlignment(ResultArg);
2392-
llvm::Type *ResTy = CGF.getTypes().ConvertType(ResultQTy);
2451+
llvm::Type *ResTy = ResultIsCap ? llvm::IntegerType::get(CGF.getLLVMContext(),
2452+
ResultInfo.Width)
2453+
: CGF.getTypes().ConvertType(ResultQTy);
23932454
unsigned OpWidth = std::max(Op1Info.Width, Op2Info.Width);
23942455

23952456
// Take the absolute value of the signed operand.
@@ -2428,8 +2489,7 @@ EmitCheckedMixedSignMultiply(CodeGenFunction &CGF, const clang::Expr *Op1,
24282489
IsNegative, CGF.Builder.CreateIsNotNull(UnsignedResult));
24292490
Overflow = CGF.Builder.CreateOr(UnsignedOverflow, Underflow);
24302491
if (ResultInfo.Width < OpWidth) {
2431-
auto IntMax =
2432-
llvm::APInt::getMaxValue(ResultInfo.Width).zext(OpWidth);
2492+
auto IntMax = llvm::APInt::getMaxValue(ResultInfo.Width).zext(OpWidth);
24332493
llvm::Value *TruncOverflow = CGF.Builder.CreateICmpUGT(
24342494
UnsignedResult, llvm::ConstantInt::get(OpTy, IntMax));
24352495
Overflow = CGF.Builder.CreateOr(Overflow, TruncOverflow);
@@ -2443,6 +2503,9 @@ EmitCheckedMixedSignMultiply(CodeGenFunction &CGF, const clang::Expr *Op1,
24432503
}
24442504
assert(Overflow && Result && "Missing overflow or result");
24452505

2506+
if (ResultIsCap)
2507+
Result = CGF.setCapabilityIntegerValue(ProvenanceCap, Result, Loc);
2508+
24462509
bool isVolatile =
24472510
ResultArg->getType()->getPointeeType().isVolatileQualified();
24482511
CGF.Builder.CreateStore(CGF.EmitToMemory(Result, ResultQTy), ResultPtr,
@@ -5495,13 +5558,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
54955558
const clang::Expr *RightArg = E->getArg(1);
54965559
const clang::Expr *ResultArg = E->getArg(2);
54975560

5561+
clang::QualType LeftQTy = LeftArg->getType();
5562+
clang::QualType RightQTy = RightArg->getType();
54985563
clang::QualType ResultQTy =
54995564
ResultArg->getType()->castAs<PointerType>()->getPointeeType();
55005565

5566+
bool LeftIsCap = LeftQTy->isCHERICapabilityType(CGM.getContext());
5567+
bool RightIsCap = RightQTy->isCHERICapabilityType(CGM.getContext());
5568+
bool ResultIsCap = ResultQTy->isCHERICapabilityType(CGM.getContext());
55015569
WidthAndSignedness LeftInfo =
5502-
getIntegerWidthAndSignedness(CGM.getContext(), LeftArg->getType());
5570+
getIntegerWidthAndSignedness(CGM.getContext(), LeftQTy);
55035571
WidthAndSignedness RightInfo =
5504-
getIntegerWidthAndSignedness(CGM.getContext(), RightArg->getType());
5572+
getIntegerWidthAndSignedness(CGM.getContext(), RightQTy);
55055573
WidthAndSignedness ResultInfo =
55065574
getIntegerWidthAndSignedness(CGM.getContext(), ResultQTy);
55075575

@@ -5510,44 +5578,78 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
55105578
if (isSpecialMixedSignMultiply(BuiltinID, LeftInfo, RightInfo, ResultInfo))
55115579
return EmitCheckedMixedSignMultiply(*this, LeftArg, LeftInfo, RightArg,
55125580
RightInfo, ResultArg, ResultQTy,
5513-
ResultInfo);
5581+
ResultInfo, E->getExprLoc());
55145582

55155583
if (isSpecialUnsignedMultiplySignedResult(BuiltinID, LeftInfo, RightInfo,
55165584
ResultInfo))
55175585
return EmitCheckedUnsignedMultiplySignedResult(
55185586
*this, LeftArg, LeftInfo, RightArg, RightInfo, ResultArg, ResultQTy,
5519-
ResultInfo);
5587+
ResultInfo, E->getExprLoc());
55205588

55215589
WidthAndSignedness EncompassingInfo =
55225590
EncompassingIntegerType({LeftInfo, RightInfo, ResultInfo});
55235591

55245592
llvm::Type *EncompassingLLVMTy =
55255593
llvm::IntegerType::get(CGM.getLLVMContext(), EncompassingInfo.Width);
55265594

5527-
llvm::Type *ResultLLVMTy = CGM.getTypes().ConvertType(ResultQTy);
5595+
llvm::Type *ResultLLVMTy =
5596+
ResultIsCap
5597+
? llvm::IntegerType::get(CGM.getLLVMContext(), ResultInfo.Width)
5598+
: CGM.getTypes().ConvertType(ResultQTy);
55285599

55295600
Intrinsic::ID IntrinsicId;
5601+
bool Commutative;
55305602
switch (BuiltinID) {
55315603
default:
55325604
llvm_unreachable("Unknown overflow builtin id.");
55335605
case Builtin::BI__builtin_add_overflow:
55345606
IntrinsicId = EncompassingInfo.Signed ? Intrinsic::sadd_with_overflow
55355607
: Intrinsic::uadd_with_overflow;
5608+
Commutative = true;
55365609
break;
55375610
case Builtin::BI__builtin_sub_overflow:
55385611
IntrinsicId = EncompassingInfo.Signed ? Intrinsic::ssub_with_overflow
55395612
: Intrinsic::usub_with_overflow;
5613+
Commutative = false;
55405614
break;
55415615
case Builtin::BI__builtin_mul_overflow:
55425616
IntrinsicId = EncompassingInfo.Signed ? Intrinsic::smul_with_overflow
55435617
: Intrinsic::umul_with_overflow;
5618+
Commutative = true;
55445619
break;
55455620
}
55465621

55475622
llvm::Value *Left = EmitScalarExpr(LeftArg);
55485623
llvm::Value *Right = EmitScalarExpr(RightArg);
55495624
Address ResultPtr = EmitPointerWithAlignment(ResultArg);
55505625

5626+
llvm::Value *ProvenanceCap = nullptr;
5627+
if (ResultIsCap) {
5628+
if (!Commutative) {
5629+
if (LeftIsCap)
5630+
ProvenanceCap = Left;
5631+
else
5632+
ProvenanceCap = llvm::ConstantPointerNull::get(Int8CheriCapTy);
5633+
} else {
5634+
bool LeftNoProvenance =
5635+
!LeftIsCap || LeftQTy->hasAttr(attr::CHERINoProvenance);
5636+
bool RightNoProvenance =
5637+
!RightIsCap || RightQTy->hasAttr(attr::CHERINoProvenance);
5638+
if (LeftNoProvenance && RightNoProvenance)
5639+
ProvenanceCap = llvm::ConstantPointerNull::get(Int8CheriCapTy);
5640+
else if (LeftNoProvenance)
5641+
ProvenanceCap = Right;
5642+
else
5643+
ProvenanceCap = Left;
5644+
}
5645+
}
5646+
5647+
if (LeftIsCap)
5648+
Left = getCapabilityIntegerValue(Left);
5649+
5650+
if (RightIsCap)
5651+
Right = getCapabilityIntegerValue(Right);
5652+
55515653
// Extend each operand to the encompassing type.
55525654
Left = Builder.CreateIntCast(Left, EncompassingLLVMTy, LeftInfo.Signed);
55535655
Right = Builder.CreateIntCast(Right, EncompassingLLVMTy, RightInfo.Signed);
@@ -5572,6 +5674,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
55725674
Result = ResultTrunc;
55735675
}
55745676

5677+
if (ResultIsCap)
5678+
Result =
5679+
setCapabilityIntegerValue(ProvenanceCap, Result, E->getExprLoc());
5680+
55755681
// Finally, store the result using the pointer.
55765682
bool isVolatile =
55775683
ResultArg->getType()->getPointeeType().isVolatileQualified();

clang/lib/Sema/SemaChecking.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@
3939
#include "clang/AST/TypeLoc.h"
4040
#include "clang/AST/UnresolvedSet.h"
4141
#include "clang/Basic/AddressSpaces.h"
42+
#include "clang/Basic/Builtins.h"
43+
#include "clang/Basic/CharInfo.h"
4244
#include "clang/Basic/Diagnostic.h"
45+
#include "clang/Basic/DiagnosticFrontend.h"
4346
#include "clang/Basic/IdentifierTable.h"
4447
#include "clang/Basic/LLVM.h"
4548
#include "clang/Basic/LangOptions.h"
@@ -569,6 +572,18 @@ static bool BuiltinOverflow(Sema &S, CallExpr *TheCall, unsigned BuiltinID) {
569572
}
570573
}
571574

575+
// ScalarExprEmitter::EmitSub's diagnostics aren't included here since
576+
// they're generally unhelpful, grouped under pedantic warnings, and would be
577+
// confusing without also taking into account the type of the result.
578+
if (BuiltinID != Builtin::BI__builtin_sub_overflow) {
579+
assert((BuiltinID == Builtin::BI__builtin_add_overflow ||
580+
BuiltinID == Builtin::BI__builtin_mul_overflow) &&
581+
"Unexpected overflow builtin");
582+
583+
S.DiagnoseAmbiguousProvenance(TheCall->getArg(0), TheCall->getArg(1),
584+
TheCall->getExprLoc(), false);
585+
}
586+
572587
return false;
573588
}
574589

0 commit comments

Comments
 (0)