Skip to content

Commit 422b272

Browse files
alexmarkovCommit Queue
authored andcommitted
Essential CFG IR instructions
Issue: #61635 Change-Id: I7de7c4a3bd7008c03c81243c73a71c2018ab7c70 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/459382 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
1 parent 8210d84 commit 422b272

File tree

7 files changed

+2415
-0
lines changed

7 files changed

+2415
-0
lines changed

pkg/cfg/lib/ir/constant_value.dart

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:cfg/ir/instructions.dart';
6+
import 'package:cfg/ir/global_context.dart';
7+
import 'package:cfg/ir/types.dart';
8+
import 'package:cfg/utils/misc.dart';
9+
import 'package:kernel/ast.dart' as ast;
10+
import 'package:kernel/src/printer.dart' as ast_printer show AstPrinter;
11+
import 'package:kernel/type_environment.dart' show StaticTypeContext;
12+
13+
/// Represents an arbitrary constant value.
14+
///
15+
/// [ConstantValue] is a thin wrapper around [ast.Constant].
16+
/// Constants which do not have corresponding representation in AST
17+
/// (e.g. constant type arguments) have dedicated subclasses of
18+
/// [ast.AuxiliaryConstant].
19+
extension type ConstantValue(ast.Constant constant) {
20+
factory ConstantValue.fromInt(int value) =>
21+
ConstantValue(ast.IntConstant(value));
22+
factory ConstantValue.fromDouble(double value) =>
23+
ConstantValue(ast.DoubleConstant(value));
24+
factory ConstantValue.fromBool(bool value) =>
25+
ConstantValue(ast.BoolConstant(value));
26+
factory ConstantValue.fromNull() => ConstantValue(ast.NullConstant());
27+
factory ConstantValue.fromString(String value) =>
28+
ConstantValue(ast.StringConstant(value));
29+
30+
int get intValue => (constant as ast.IntConstant).value;
31+
double get doubleValue => (constant as ast.DoubleConstant).value;
32+
bool get boolValue => (constant as ast.BoolConstant).value;
33+
String get stringValue => (constant as ast.StringConstant).value;
34+
35+
bool get isInt => constant is ast.IntConstant;
36+
bool get isDouble => constant is ast.DoubleConstant;
37+
bool get isBool => constant is ast.BoolConstant;
38+
bool get isNull => constant is ast.NullConstant;
39+
bool get isString => constant is ast.StringConstant;
40+
41+
CType get type => switch (constant) {
42+
ast.IntConstant() => const IntType(),
43+
ast.DoubleConstant() => const DoubleType(),
44+
ast.BoolConstant() => const BoolType(),
45+
ast.NullConstant() => const NullType(),
46+
ast.StringConstant() => const StringType(),
47+
TypeArgumentsConstant() => const TypeArgumentsType(),
48+
_ => StaticType(
49+
constant.getType(GlobalContext.instance.staticTypeContextForConstants),
50+
),
51+
};
52+
53+
bool get isZero => switch (constant) {
54+
ast.IntConstant(:var value) => value == 0,
55+
ast.DoubleConstant(:var value) => value == 0.0,
56+
_ => false,
57+
};
58+
59+
bool get isNegative => switch (constant) {
60+
ast.IntConstant(:var value) => value < 0,
61+
ast.DoubleConstant(:var value) => value.isNegative,
62+
_ => false,
63+
};
64+
65+
String valueToString() => switch (constant) {
66+
ast.StringConstant(:var value) => '"${value}"',
67+
ast.PrimitiveConstant(:var value) => value.toString(),
68+
_ => constant.toString(),
69+
};
70+
}
71+
72+
/// Utility class to perform operations on constant values.
73+
///
74+
/// Methods of this class return `null` when constant folding
75+
/// cannot be performed (e.g. corresponding operation would
76+
/// throw an exception at runtime).
77+
class ConstantFolding {
78+
const ConstantFolding();
79+
80+
ConstantValue comparison(
81+
ComparisonOpcode op,
82+
ConstantValue left,
83+
ConstantValue right,
84+
) {
85+
final result = switch (op) {
86+
ComparisonOpcode.equal => left.constant == right.constant,
87+
ComparisonOpcode.notEqual => left.constant != right.constant,
88+
ComparisonOpcode.identical => left.constant == right.constant,
89+
ComparisonOpcode.notIdentical => left.constant != right.constant,
90+
ComparisonOpcode.intEqual => left.intValue == right.intValue,
91+
ComparisonOpcode.intNotEqual => left.intValue != right.intValue,
92+
ComparisonOpcode.intLess => left.intValue < right.intValue,
93+
ComparisonOpcode.intLessOrEqual => left.intValue <= right.intValue,
94+
ComparisonOpcode.intGreater => left.intValue > right.intValue,
95+
ComparisonOpcode.intGreaterOrEqual => left.intValue >= right.intValue,
96+
ComparisonOpcode.intTestIsZero => (left.intValue & right.intValue) == 0,
97+
ComparisonOpcode.intTestIsNotZero =>
98+
(left.intValue & right.intValue) != 0,
99+
ComparisonOpcode.doubleEqual => left.doubleValue == right.doubleValue,
100+
ComparisonOpcode.doubleNotEqual => left.doubleValue != right.doubleValue,
101+
ComparisonOpcode.doubleLess => left.doubleValue < right.doubleValue,
102+
ComparisonOpcode.doubleLessOrEqual =>
103+
left.doubleValue <= right.doubleValue,
104+
ComparisonOpcode.doubleGreater => left.doubleValue > right.doubleValue,
105+
ComparisonOpcode.doubleGreaterOrEqual =>
106+
left.doubleValue >= right.doubleValue,
107+
};
108+
return ConstantValue.fromBool(result);
109+
}
110+
111+
ConstantValue? binaryIntOp(
112+
BinaryIntOpcode op,
113+
ConstantValue left,
114+
ConstantValue right,
115+
) {
116+
final a = left.intValue;
117+
final b = right.intValue;
118+
switch (op) {
119+
case BinaryIntOpcode.add:
120+
return ConstantValue.fromInt(a + b);
121+
case BinaryIntOpcode.sub:
122+
return ConstantValue.fromInt(a - b);
123+
case BinaryIntOpcode.mul:
124+
return ConstantValue.fromInt(a * b);
125+
case BinaryIntOpcode.truncatingDiv:
126+
return (b != 0) ? ConstantValue.fromInt(a ~/ b) : null;
127+
case BinaryIntOpcode.mod:
128+
return (b != 0) ? ConstantValue.fromInt(a % b) : null;
129+
case BinaryIntOpcode.rem:
130+
return (b != 0) ? ConstantValue.fromInt(a.remainder(b)) : null;
131+
case BinaryIntOpcode.bitOr:
132+
return ConstantValue.fromInt(a | b);
133+
case BinaryIntOpcode.bitAnd:
134+
return ConstantValue.fromInt(a & b);
135+
case BinaryIntOpcode.bitXor:
136+
return ConstantValue.fromInt(a ^ b);
137+
case BinaryIntOpcode.shiftLeft:
138+
return (b >= 0) ? ConstantValue.fromInt(a << b) : null;
139+
case BinaryIntOpcode.shiftRight:
140+
return (b >= 0) ? ConstantValue.fromInt(a >> b) : null;
141+
case BinaryIntOpcode.unsignedShiftRight:
142+
return (b >= 0) ? ConstantValue.fromInt(a >>> b) : null;
143+
}
144+
}
145+
146+
ConstantValue? unaryIntOp(UnaryIntOpcode op, ConstantValue operand) {
147+
final x = operand.intValue;
148+
switch (op) {
149+
case UnaryIntOpcode.neg:
150+
return ConstantValue.fromInt(-x);
151+
case UnaryIntOpcode.bitNot:
152+
return ConstantValue.fromInt(~x);
153+
case UnaryIntOpcode.toDouble:
154+
return ConstantValue.fromDouble(x.toDouble());
155+
case UnaryIntOpcode.abs:
156+
return ConstantValue.fromInt(x.abs());
157+
case UnaryIntOpcode.sign:
158+
return ConstantValue.fromInt(x.sign);
159+
}
160+
}
161+
162+
ConstantValue? binaryDoubleOp(
163+
BinaryDoubleOpcode op,
164+
ConstantValue left,
165+
ConstantValue right,
166+
) {
167+
final a = left.doubleValue;
168+
final b = right.doubleValue;
169+
switch (op) {
170+
case BinaryDoubleOpcode.add:
171+
return ConstantValue.fromDouble(a + b);
172+
case BinaryDoubleOpcode.sub:
173+
return ConstantValue.fromDouble(a - b);
174+
case BinaryDoubleOpcode.mul:
175+
return ConstantValue.fromDouble(a * b);
176+
case BinaryDoubleOpcode.mod:
177+
return ConstantValue.fromDouble(a % b);
178+
case BinaryDoubleOpcode.rem:
179+
return ConstantValue.fromDouble(a.remainder(b));
180+
case BinaryDoubleOpcode.div:
181+
return ConstantValue.fromDouble(a / b);
182+
case BinaryDoubleOpcode.truncatingDiv:
183+
final doubleResult = a / b;
184+
return doubleResult.isFinite
185+
? ConstantValue.fromInt(doubleResult.truncate())
186+
: null;
187+
}
188+
}
189+
190+
ConstantValue? unaryDoubleOp(UnaryDoubleOpcode op, ConstantValue operand) {
191+
final x = operand.doubleValue;
192+
switch (op) {
193+
case UnaryDoubleOpcode.neg:
194+
return ConstantValue.fromDouble(-x);
195+
case UnaryDoubleOpcode.abs:
196+
return ConstantValue.fromDouble(x.abs());
197+
case UnaryDoubleOpcode.sign:
198+
return ConstantValue.fromDouble(x.sign);
199+
case UnaryDoubleOpcode.square:
200+
return ConstantValue.fromDouble(x * x);
201+
case UnaryDoubleOpcode.round:
202+
return x.isFinite ? ConstantValue.fromInt(x.round()) : null;
203+
case UnaryDoubleOpcode.floor:
204+
return x.isFinite ? ConstantValue.fromInt(x.floor()) : null;
205+
case UnaryDoubleOpcode.ceil:
206+
return x.isFinite ? ConstantValue.fromInt(x.ceil()) : null;
207+
case UnaryDoubleOpcode.truncate:
208+
return x.isFinite ? ConstantValue.fromInt(x.truncate()) : null;
209+
case UnaryDoubleOpcode.roundToDouble:
210+
return ConstantValue.fromDouble(x.roundToDouble());
211+
case UnaryDoubleOpcode.floorToDouble:
212+
return ConstantValue.fromDouble(x.floorToDouble());
213+
case UnaryDoubleOpcode.ceilToDouble:
214+
return ConstantValue.fromDouble(x.ceilToDouble());
215+
case UnaryDoubleOpcode.truncateToDouble:
216+
return ConstantValue.fromDouble(x.truncateToDouble());
217+
}
218+
}
219+
}
220+
221+
/// Constant type arguments.
222+
class TypeArgumentsConstant extends ast.AuxiliaryConstant {
223+
final List<ast.DartType> types;
224+
225+
TypeArgumentsConstant(this.types);
226+
227+
@override
228+
void visitChildren(ast.Visitor v) {
229+
ast.visitList(types, v);
230+
}
231+
232+
@override
233+
void toTextInternal(ast_printer.AstPrinter printer) {
234+
printer.writeTypeArguments(types);
235+
}
236+
237+
@override
238+
String toString() => toStringInternal();
239+
240+
@override
241+
int get hashCode => listHashCode(types);
242+
243+
@override
244+
bool operator ==(Object other) {
245+
return other is TypeArgumentsConstant &&
246+
listEquals(this.types, other.types);
247+
}
248+
249+
@override
250+
ast.DartType getType(StaticTypeContext context) => const ast.DynamicType();
251+
}

0 commit comments

Comments
 (0)