22
33public import Veir.Analysis.DataFlow.Domains.KnownBitsDomain
44public import Veir.Analysis.DataFlow.SparseForwardDataFlowAnalysis
5+ public import Veir.Interpreter.Evaluate
56
67import Veir.Interfaces.FoldInterfaces
8+ import Veir.Meta.BVDecide
79
810public section
911
@@ -18,6 +20,118 @@ operations in the Arith, Comb, and LLVM dialects. Other integer-producing operat
1820conservatively produce an unknown value of the result width.
1921-/
2022
23+ namespace KnownBits
24+
25+ /-- Known bits produced by bitwise AND. -/
26+ def bitwiseAnd? (lhs rhs : KnownBits) : Option KnownBits :=
27+ if h : lhs.bitwidth = rhs.bitwidth then
28+ let rhsZero := h ▸ rhs.zero
29+ let rhsOne := h ▸ rhs.one
30+ some
31+ { bitwidth := lhs.bitwidth
32+ zero := lhs.zero ||| rhsZero
33+ one := lhs.one &&& rhsOne }
34+ else
35+ none
36+
37+ /-- Known bits produced by bitwise OR. -/
38+ def bitwiseOr? (lhs rhs : KnownBits) : Option KnownBits :=
39+ if h : lhs.bitwidth = rhs.bitwidth then
40+ let rhsZero := h ▸ rhs.zero
41+ let rhsOne := h ▸ rhs.one
42+ some
43+ { bitwidth := lhs.bitwidth
44+ zero := lhs.zero &&& rhsZero
45+ one := lhs.one ||| rhsOne }
46+ else
47+ none
48+
49+ /-- Known bits produced by bitwise XOR. -/
50+ def bitwiseXor? (lhs rhs : KnownBits) : Option KnownBits :=
51+ if h : lhs.bitwidth = rhs.bitwidth then
52+ let rhsZero := h ▸ rhs.zero
53+ let rhsOne := h ▸ rhs.one
54+ some
55+ { bitwidth := lhs.bitwidth
56+ zero := (lhs.zero &&& rhsZero) ||| (lhs.one &&& rhsOne)
57+ one := (lhs.zero &&& rhsOne) ||| (lhs.one &&& rhsZero) }
58+ else
59+ none
60+
61+ end KnownBits
62+
63+ namespace KnownBitsLattice
64+
65+ /-- Transfer known bits through bitwise AND. -/
66+ def bitwiseAnd : KnownBitsLattice → KnownBitsLattice → KnownBitsLattice
67+ | .bottom, _ | _, .bottom => .bottom
68+ | .top, .top => .top
69+ | .known lhs, .top | .top, .known lhs =>
70+ .known { lhs with one := 0 }
71+ | .known lhs, .known rhs =>
72+ match lhs.bitwiseAnd? rhs with
73+ | some bits => .known bits
74+ | none => .top
75+
76+ /--
77+ Known-bits AND soundly over-approximates every result produced by the LLVM
78+ interpreter from concrete values represented by its abstract operands.
79+ -/
80+ theorem bitwiseAnd_sound
81+ (lhs rhs : KnownBitsLattice)
82+ (bitwidth : Nat)
83+ (lhsValue rhsValue resultValue : BitVec bitwidth)
84+ (hlhs : RuntimeValue.int bitwidth (.val lhsValue) ∈ γ lhs)
85+ (hrhs : RuntimeValue.int bitwidth (.val rhsValue) ∈ γ rhs)
86+ (heval :
87+ foldEvaluate (.llvm .and) () #[IntegerType.mk bitwidth]
88+ #[.int bitwidth (.val lhsValue), .int bitwidth (.val rhsValue)] =
89+ .ok #[.int bitwidth (.val resultValue)]) :
90+ RuntimeValue.int bitwidth (.val resultValue) ∈ γ (bitwiseAnd lhs rhs) := by
91+ obtain rfl : resultValue = lhsValue &&& rhsValue := by
92+ simpa [foldEvaluate_llvm_and] using heval.symm
93+ cases lhs <;> cases rhs <;> simp_all only [not_mem_γ_bottom, mem_γ_top, bitwiseAnd]
94+ case known.top | top.known =>
95+ first
96+ | (obtain ⟨zero, _, rfl, hzero, _⟩ := mem_γ_known_iff.mp hlhs)
97+ | (obtain ⟨zero, _, rfl, hzero, _⟩ := mem_γ_known_iff.mp hrhs)
98+ refine mem_γ_known_iff.mpr
99+ ⟨zero, 0 , rfl, fun i hi h => by simp [hzero i hi h], by simp⟩
100+ case known.known =>
101+ obtain ⟨lhsZero, lhsOne, rfl, hlzero, hlone⟩ := mem_γ_known_iff.mp hlhs
102+ obtain ⟨rhsZero, rhsOne, rfl, hrzero, hrone⟩ := mem_γ_known_iff.mp hrhs
103+ simp only [KnownBits.bitwiseAnd?]
104+ refine mem_γ_known_iff.mpr
105+ ⟨lhsZero ||| rhsZero, lhsOne &&& rhsOne, rfl, ?_,
106+ fun i hi h => by simp_all [hlone i hi, hrone i hi]⟩
107+ · intro i hi hresultZero
108+ specialize hlzero i hi
109+ specialize hrzero i hi
110+ simp_all <;> veir_bv_decide
111+
112+ /-- Transfer known bits through bitwise OR. -/
113+ def bitwiseOr : KnownBitsLattice → KnownBitsLattice → KnownBitsLattice
114+ | .bottom, _ | _, .bottom => .bottom
115+ | .top, .top => .top
116+ | .known lhs, .top | .top, .known lhs =>
117+ .known { lhs with zero := 0 }
118+ | .known lhs, .known rhs =>
119+ match lhs.bitwiseOr? rhs with
120+ | some bits => .known bits
121+ | none => .top
122+
123+ /-- Transfer known bits through bitwise XOR. -/
124+ def bitwiseXor : KnownBitsLattice → KnownBitsLattice → KnownBitsLattice
125+ | .bottom, _ | _, .bottom => .bottom
126+ | .top, .top => .top
127+ | .known lhs, .top | .top, .known lhs => .unknown lhs.bitwidth
128+ | .known lhs, .known rhs =>
129+ match lhs.bitwiseXor? rhs with
130+ | some bits => .known bits
131+ | none => .top
132+
133+ end KnownBitsLattice
134+
21135namespace KnownBitsAnalysis
22136
23137instance : SparseFactSpec .knownBits KnownBitsLattice where
@@ -26,10 +140,10 @@ instance : SparseFactSpec .knownBits KnownBitsLattice where
26140private def transferBitwise
27141 (operation : KnownBitsLattice → KnownBitsLattice → KnownBitsLattice)
28142 (numResults : Nat)
29- (operands : Array KnownBitsLattice) : Array (Option KnownBitsLattice) :=
143+ (operands : Array KnownBitsLattice) : Array KnownBitsLattice :=
30144 match operands.toList with
31- | [] => Array.replicate numResults none
32- | first :: rest => Array.replicate numResults (some ( rest.foldl operation first) )
145+ | [] => Array.replicate numResults ⊥
146+ | first :: rest => Array.replicate numResults (rest.foldl operation first)
33147
34148/--
35149Infer known bits for one operation. Bottom operands cause the transfer to wait for
@@ -38,18 +152,18 @@ more information; unsupported integer results receive a width-aware unknown valu
38152def transfer
39153 (op : OperationPtr)
40154 (operands : Array KnownBitsLattice)
41- (irCtx : WfIRContext OpCode) : Array (Option KnownBitsLattice) :=
155+ (irCtx : WfIRContext OpCode) : Array KnownBitsLattice :=
42156 let numResults := op.getNumResults! irCtx.raw
43157 let resultTypes := op.getResultTypes! irCtx.raw
44158 let pessimisticUpdates := resultTypes.map fun resultType =>
45159 match resultType.val with
46- | .integerType intType => some ( .unknown intType.bitwidth)
47- | _ => none
160+ | .integerType intType => .unknown intType.bitwidth
161+ | _ => ⊥
48162
49163 if op.getNumRegions! irCtx.raw ≠ 0 then
50164 pessimisticUpdates
51165 else if operands.any (· = ⊥) then
52- Array.replicate numResults none
166+ Array.replicate numResults ⊥
53167 else
54168 let opType := op.getOpType! irCtx.raw
55169 let exactOperands := operands.map fun
@@ -63,31 +177,31 @@ def transfer
63177 | some results =>
64178 (results.zip resultTypes).map fun (result, resultType) =>
65179 match resultType.val, result with
66- | .integerType _, .useOperand index => some ( operands[index]?.getD ⊤)
180+ | .integerType _, .useOperand index => operands[index]?.getD ⊤
67181 | .integerType intType, .useConstant (.int bitwidth (.val value)) =>
68182 if h : bitwidth = intType.bitwidth then
69183 let value := value.cast h
70- some ( .known { bitwidth := intType.bitwidth, zero := ~~~value, one := value })
184+ .known { bitwidth := intType.bitwidth, zero := ~~~value, one := value }
71185 else
72- some ⊤
73- | .integerType _, .useConstant _ => some ⊤
74- | _, _ => none
186+ ⊤
187+ | .integerType _, .useConstant _ => ⊤
188+ | _, _ => ⊥
75189 | none =>
76190 match opType with
77191 | OpCode.arith Arith.constant =>
78192 let props := op.getProperties! irCtx.raw (OpCode.arith Arith.constant)
79193 Array.replicate numResults
80- (some ( .constant props.value.type.bitwidth props.value.value) )
194+ (.constant props.value.type.bitwidth props.value.value)
81195 | OpCode.llvm Llvm.mlir__constant =>
82196 let props := op.getProperties! irCtx.raw (OpCode.llvm Llvm.mlir__constant)
83197 match props.value with
84198 | .integer attr =>
85- Array.replicate numResults (some ( .constant attr.type.bitwidth attr.value) )
199+ Array.replicate numResults (.constant attr.type.bitwidth attr.value)
86200 | _ => pessimisticUpdates
87201 | OpCode.hw HW.constant =>
88202 let props := op.getProperties! irCtx.raw (OpCode.hw HW.constant)
89203 Array.replicate numResults
90- (some ( .constant props.value.type.bitwidth props.value.value) )
204+ (.constant props.value.type.bitwidth props.value.value)
91205 | OpCode.arith Arith.andi
92206 | OpCode.llvm Llvm.and
93207 | OpCode.comb Comb.and =>
0 commit comments