23makeInfluenceInfo(
Influence influence, std::optional<Location> loc = std::nullopt) {
26 if (loc.has_value()) {
36ForbiddenInfluenceAnalyzer::AnalysisFrame::AnalysisFrame(
38 llvm::ArrayRef<InfluenceInfo> argInfluenceInfos,
InfluenceInfo inheritedControl
40 : analyzer(parentAnalyzer), inheritedControlInfluence(inheritedControl) {
41 Region *region = callableOp.getCallableRegion();
42 assert(region && !region->empty() &&
"callable must have a body");
43 Block &entry = region->front();
44 for (
auto [arg, influenceInfo] : llvm::zip(entry.getArguments(), argInfluenceInfos)) {
45 valueCache[arg] = influenceInfo;
49InfluenceInfo ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeValue(Value value) {
50 if (
auto it = valueCache.find(value); it != valueCache.end()) {
53 if (!activeValues.insert(value).second) {
58 if (
auto blockArg = llvm::dyn_cast<BlockArgument>(value)) {
59 result = analyzeBlockArgument(blockArg);
60 }
else if (Operation *defOp = value.getDefiningOp()) {
61 auto opRes = llvm::dyn_cast<OpResult>(value);
62 assert(opRes &&
"value has defining op, so it must be an op result");
63 if (llvm::isa<MemberReadOp>(defOp)) {
65 }
else if (
auto call = llvm::dyn_cast<CallOpInterface>(defOp)) {
66 result = analyzeCallResult(call, opRes);
67 }
else if (
auto ifOp = llvm::dyn_cast<scf::IfOp>(defOp)) {
68 result = analyzeIfResult(ifOp, opRes);
69 }
else if (
auto forOp = llvm::dyn_cast<scf::ForOp>(defOp)) {
70 result = analyzeForResult(forOp, opRes);
71 }
else if (
auto execOp = llvm::dyn_cast<scf::ExecuteRegionOp>(defOp)) {
72 result = analyzeExecuteRegionResult(execOp, opRes);
73 }
else if (
auto whileOp = llvm::dyn_cast<scf::WhileOp>(defOp)) {
74 result = analyzeWhileResult(whileOp, opRes);
76 for (Value operand : defOp->getOperands()) {
82 activeValues.erase(value);
83 valueCache[value] = result;
87InfluenceInfo ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzePreconditionOp(
88 PreconditionOpInterface preCondOp
91 inheritedControlInfluence, analyzeValue(preCondOp.
getCondition()),
92 analyzeControlAncestors(preCondOp.getOperation())
96IncludedContractSummary
97ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeIncludeOp(IncludeOp includeOp) {
98 InfluenceInfo callerControlInfluence = analyzeControlAncestors(includeOp.getOperation());
102 SymbolTableCollection tables;
104 if (failed(calleeTarget)) {
105 IncludedContractSummary summary;
107 {.preconditionLoc = {},
115 ContractOp calleeContract = calleeTarget->get();
117 llvm::SmallVector<InfluenceInfo> argInfluences = llvm::map_to_vector(
118 includeOp.
getArgOperands(), [
this, &calleeControlInfluence](Value operand) {
119 return mergeInfluenceInfo(analyzeValue(operand), calleeControlInfluence);
122 return analyzer.analyzeIncludedContract(calleeContract, argInfluences, calleeControlInfluence);
125InfluenceInfo ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeControlAncestors(Operation *op) {
126 if (
auto it = controlAncestorCache.find(op); it != controlAncestorCache.end()) {
131 Operation *current = op;
132 while (Operation *parentOp = current->getParentOp()) {
133 if (isa<ContractOp>(parentOp)) {
140 controlAncestorCache[op] = result;
144InfluenceInfo ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeAncestorControl(
145 Operation *ancestor, Operation *nestedOp
147 if (
auto ifOp = dyn_cast<scf::IfOp>(ancestor)) {
148 return analyzeValue(ifOp.getCondition());
150 if (
auto forOp = dyn_cast<scf::ForOp>(ancestor)) {
152 analyzeValue(forOp.getLowerBound()), analyzeValue(forOp.getUpperBound()),
153 analyzeValue(forOp.getStep())
156 if (
auto whileOp = dyn_cast<scf::WhileOp>(ancestor)) {
157 InfluenceInfo result = analyzeValue(whileOp.getConditionOp().getCondition());
158 Region *nestedRegion = nestedOp->getParentRegion();
159 if (nestedRegion == &whileOp.getAfter()) {
160 unsigned beforeArgCount = whileOp.getBefore().front().getNumArguments();
161 for (
unsigned i = 0; i < beforeArgCount; ++i) {
172ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeBlockArgument(BlockArgument blockArg) {
173 Block *owner = blockArg.getOwner();
175 Operation *parentOp = owner->getParentOp();
176 if (
auto forOp = llvm::dyn_cast<scf::ForOp>(parentOp)) {
177 unsigned argNumber = blockArg.getArgNumber();
179 analyzeValue(forOp.getLowerBound()), analyzeValue(forOp.getUpperBound()),
180 analyzeValue(forOp.getStep())
187 if (argNumber != 0) {
188 unsigned iterIndex = argNumber - 1;
190 tripCountInfo, analyzeValue(forOp.getInitArgs()[iterIndex]),
191 analyzeValue(forOp.getYieldedValues()[iterIndex])
194 return tripCountInfo;
196 if (
auto whileOp = llvm::dyn_cast<scf::WhileOp>(parentOp)) {
197 Region *region = owner->getParent();
198 if (region == &whileOp.getBefore()) {
202 analyzeValue(whileOp.getInits()[blockArg.getArgNumber()]),
203 analyzeValue(whileOp.getYieldOp().getOperand(blockArg.getArgNumber()))
206 if (region == &whileOp.getAfter()) {
210 scf::ConditionOp condOp = whileOp.getConditionOp();
211 auto condArgs = condOp.getArgs();
213 condArgs.size() == region->getNumArguments() &&
214 "condition args must equal after region args"
216 return analyzeValue(condArgs[blockArg.getArgNumber()]);
222InfluenceInfo ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeCallResult(
223 CallOpInterface call, OpResult callRes
225 auto resolvedCallable = llvm::dyn_cast_if_present<CallableOpInterface>(call.resolveCallable());
226 if (!resolvedCallable || !resolvedCallable.getCallableRegion()) {
230 llvm::SmallVector<InfluenceInfo> argInfluences =
231 llvm::map_to_vector(call.getArgOperands(), [
this](Value operand) {
232 return analyzeValue(operand);
234 return analyzer.analyzeCallableResult(resolvedCallable, argInfluences, callRes.getResultNumber());
238ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeIfResult(scf::IfOp ifOp, OpResult ifRes) {
239 unsigned resultNumber = ifRes.getResultNumber();
241 for (scf::YieldOp yieldOp : {ifOp.elseYield(), ifOp.thenYield()}) {
242 if (yieldOp && yieldOp->getNumOperands() > resultNumber) {
250ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeForResult(scf::ForOp forOp, OpResult forRes) {
251 unsigned resultNumber = forRes.getResultNumber();
253 analyzeValue(forOp.getLowerBound()), analyzeValue(forOp.getUpperBound()),
254 analyzeValue(forOp.getStep()), analyzeValue(forOp.getInitArgs()[resultNumber]),
255 analyzeValue(forOp.getYieldedValues()[resultNumber])
259InfluenceInfo ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeExecuteRegionResult(
260 scf::ExecuteRegionOp execOp, OpResult execRes
262 unsigned resultNumber = execRes.getResultNumber();
264 for (Block &block : execOp.getRegion()) {
269 if (
auto yieldOp = dyn_cast<scf::YieldOp>(block.getTerminator());
270 yieldOp && yieldOp.getNumOperands() > resultNumber) {
277InfluenceInfo ForbiddenInfluenceAnalyzer::AnalysisFrame::analyzeWhileResult(
278 scf::WhileOp whileOp, OpResult whileRes
280 unsigned resultNumber = whileRes.getResultNumber();
285 auto inits = whileOp.getInits();
286 scf::ConditionOp condOp = whileOp.getConditionOp();
287 auto condArgs = condOp.getArgs();
288 auto yieldOps = whileOp.getYieldOp().getOperands();
290 inits.size() == condArgs.size() && condArgs.size() == yieldOps.size() &&
291 "invalid while op dimensions"
293 assert(inits.size() > resultNumber &&
"invalid result number");
296 analyzeValue(inits[resultNumber]), analyzeValue(condArgs[resultNumber]),
297 analyzeValue(yieldOps[resultNumber]),
299 analyzeValue(condOp.getCondition())
308 if (
auto it = cachedFrames.find(contract); it != cachedFrames.end()) {
309 return it->second.analyzeValue(value);
311 llvm::SmallVector<InfluenceInfo> argInfluenceInfos =
312 llvm::map_to_vector(contract.getArguments(), [contract](BlockArgument arg) {
313 return classifyContractArgument(contract, arg);
315 auto [it, inserted] = cachedFrames.try_emplace(contract, *
this, contract, argInfluenceInfos);
316 assert(inserted &&
"lookup failure");
317 return it->second.analyzeValue(value);
323 if (
auto it = cachedFrames.find(contract); it != cachedFrames.end()) {
324 return it->second.analyzePreconditionOp(preCondOp);
327 llvm::SmallVector<InfluenceInfo> argInfluenceInfos =
328 llvm::map_to_vector(contract.getArguments(), [contract](BlockArgument arg) {
329 return classifyContractArgument(contract, arg);
331 auto [it, inserted] = cachedFrames.try_emplace(contract, *
this, contract, argInfluenceInfos);
332 assert(inserted &&
"lookup failure");
333 return it->second.analyzePreconditionOp(preCondOp);
337 CallableOpInterface callableOp, llvm::ArrayRef<InfluenceInfo> argInfluences,
338 unsigned resultNumber
341 .callable = callableOp,
342 .argInfluences = llvm::SmallVector<InfluenceInfo>(argInfluences.begin(), argInfluences.end()),
343 .resultNumber = resultNumber,
346 if (
auto it = callableSummaryCache.find(key); it != callableSummaryCache.end()) {
349 if (!activeSummaries.insert(key).second) {
354 Region *region = callableOp.getCallableRegion();
355 if (region && !region->empty()) {
356 AnalysisFrame frame(*
this, callableOp, argInfluences);
359 if (retOp.getNumOperands() > resultNumber) {
360 summary =
mergeInfluenceInfo(summary, frame.analyzeValue(retOp.getOperand(resultNumber)));
365 activeSummaries.erase(key);
366 callableSummaryCache[key] = summary;
371 ContractOp calleeContract, llvm::ArrayRef<InfluenceInfo> argInfluences,
375 .contract = calleeContract,
376 .argInfluences = llvm::SmallVector<InfluenceInfo>(argInfluences.begin(), argInfluences.end()),
377 .inheritedControlInfluence = inheritedControlInfluence,
380 if (
auto it = includedContractSummaryCache.find(key); it != includedContractSummaryCache.end()) {
383 if (!activeIncludedSummaries.insert(key).second) {
391 AnalysisFrame frame(*
this, calleeContract, argInfluences, inheritedControlInfluence);
394 SmallVector<PreconditionOpInterface> preconditionOps =
395 walkCollect<PreconditionOpInterface>(calleeContract);
397 InfluenceInfo influenceInfo = frame.analyzePreconditionOp(preCondOp);
400 {.preconditionLoc = preCondOp->getLoc(), .influenceInfo = influenceInfo}
405 SmallVector<IncludeOp> includeOps = walkCollect<IncludeOp>(calleeContract);
411 activeIncludedSummaries.erase(key);
412 includedContractSummaryCache[key] = summary;
418 if (
auto it = cachedFrames.find(contract); it != cachedFrames.end()) {
419 return it->second.analyzeIncludeOp(includeOp);
422 llvm::SmallVector<InfluenceInfo> argInfluenceInfos =
423 llvm::map_to_vector(contract.getArguments(), [contract](BlockArgument arg) {
424 return classifyContractArgument(contract, arg);
426 auto [it, inserted] = cachedFrames.try_emplace(contract, *
this, contract, argInfluenceInfos);
427 assert(inserted &&
"lookup failure");
428 return it->second.analyzeIncludeOp(includeOp);
432ForbiddenInfluenceAnalyzer::classifyContractArgument(
ContractOp contract, BlockArgument arg) {
433 SymbolTableCollection tables;
435 if (failed(funcTarget)) {
439 unsigned numFuncInputs = funcTarget->get().getFunctionType().getNumInputs();
440 if (arg.getArgNumber() >= numFuncInputs) {
This file contains an analysis and utilities for determining if a verif precondition is dependent,...
::mlir::FailureOr< SymbolLookupResult< function::FuncDefOp > > getFuncTarget(::mlir::SymbolTableCollection &tables)
Return the FuncDefOp that this contract targets, or failure if it does not target a function or the f...
::mlir::Operation::operand_range getArgOperands()
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::verif::ContractOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target Contract for this CallOp.
::mlir::TypedValue<::mlir::IntegerType > getCondition()
Gets the SSA Value for the condition operand.
Interprocedural verifier-local analysis for forbidden precondition influence.
InfluenceInfo analyzeCallableResult(mlir::CallableOpInterface callableOp, llvm::ArrayRef< InfluenceInfo > argInfluences, unsigned resultNumber)
Summarize the forbidden influence of one callable result under the given argument influences.
IncludedContractSummary analyzeIncludedOp(verif::ContractOp contract, verif::IncludeOp includeOp)
Check whether an include op becomes invalid under its caller's operand bindings and enclosing SCF con...
InfluenceInfo analyzePreconditionOp(verif::ContractOp contract, verif::PreconditionOpInterface preCondOp)
Classify the forbidden influence reaching a precondition op, including both the condition operand and...
InfluenceInfo analyzeContractValue(verif::ContractOp contract, mlir::Value value)
Classify the forbidden influence reaching a value inside a contract body.
IncludedContractSummary analyzeIncludedContract(verif::ContractOp calleeContract, llvm::ArrayRef< InfluenceInfo > argInfluences, InfluenceInfo inheritedControlInfluence=InfluenceInfo::None())
Check whether an included contract becomes invalid under caller-provided operand influences,...
ForbiddenPreconditionInfluence Influence
ForbiddenPreconditionInfluenceInfo InfluenceInfo
ForbiddenPreconditionInfluenceInfo mergeInfluenceInfo(ForbiddenPreconditionInfluenceInfo lhs, const ForbiddenPreconditionInfluenceInfo &rhs)
Merge two forbidden-influence summaries, preserving the first known source location for each forbidde...
bool any(ForbiddenPreconditionInfluence influence)
Return true when the influence set contains at least one classification.
ForbiddenPreconditionInfluence influence
llvm::SmallSetVector< mlir::Location, 2 > structMemberLocs
Cache key for one interprocedural callable-result summary query.
Cache key for one interprocedural included-contract summary query.
Summary of all included-contract precondition failures under a specific caller binding.
llvm::SmallVector< IncludedContractFailure > failures