• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/compiler/simplified-lowering-verifier.h"
6 
7 #include "src/compiler/operation-typer.h"
8 #include "src/compiler/type-cache.h"
9 
10 namespace v8 {
11 namespace internal {
12 namespace compiler {
13 
LeastGeneralTruncation(const Truncation & t1,const Truncation & t2)14 Truncation LeastGeneralTruncation(const Truncation& t1, const Truncation& t2) {
15   if (t1.IsLessGeneralThan(t2)) return t1;
16   CHECK(t2.IsLessGeneralThan(t1));
17   return t2;
18 }
19 
LeastGeneralTruncation(const Truncation & t1,const Truncation & t2,const Truncation & t3)20 Truncation LeastGeneralTruncation(const Truncation& t1, const Truncation& t2,
21                                   const Truncation& t3) {
22   return LeastGeneralTruncation(LeastGeneralTruncation(t1, t2), t3);
23 }
24 
CheckType(Node * node,const Type & type)25 void SimplifiedLoweringVerifier::CheckType(Node* node, const Type& type) {
26   CHECK(NodeProperties::IsTyped(node));
27   Type node_type = NodeProperties::GetType(node);
28   if (!type.Is(node_type)) {
29     std::ostringstream type_str;
30     type.PrintTo(type_str);
31     std::ostringstream node_type_str;
32     node_type.PrintTo(node_type_str);
33 
34     FATAL(
35         "SimplifiedLoweringVerifierError: verified type %s of node #%d:%s "
36         "does not match with type %s assigned during lowering",
37         type_str.str().c_str(), node->id(), node->op()->mnemonic(),
38         node_type_str.str().c_str());
39   }
40 }
41 
CheckAndSet(Node * node,const Type & type,const Truncation & trunc)42 void SimplifiedLoweringVerifier::CheckAndSet(Node* node, const Type& type,
43                                              const Truncation& trunc) {
44   DCHECK(!type.IsInvalid());
45 
46   if (NodeProperties::IsTyped(node)) {
47     CheckType(node, type);
48   } else {
49     // We store the type inferred by the verification pass. We do not update
50     // the node's type directly, because following phases might encounter
51     // unsound types as long as the verification is not complete.
52     SetType(node, type);
53   }
54   SetTruncation(node, GeneralizeTruncation(trunc, type));
55 }
56 
IsModuloTruncation(const Truncation & truncation)57 bool IsModuloTruncation(const Truncation& truncation) {
58   return truncation.IsUsedAsWord32() || truncation.IsUsedAsWord64() ||
59          Truncation::Any().IsLessGeneralThan(truncation);
60 }
61 
GeneralizeTruncation(const Truncation & truncation,const Type & type) const62 Truncation SimplifiedLoweringVerifier::GeneralizeTruncation(
63     const Truncation& truncation, const Type& type) const {
64   IdentifyZeros identify_zeros = truncation.identify_zeros();
65   if (!type.Maybe(Type::MinusZero())) {
66     identify_zeros = IdentifyZeros::kDistinguishZeros;
67   }
68 
69   switch (truncation.kind()) {
70     case Truncation::TruncationKind::kAny: {
71       return Truncation::Any(identify_zeros);
72     }
73     case Truncation::TruncationKind::kWord32: {
74       if (type.Is(Type::Signed32OrMinusZero()) ||
75           type.Is(Type::Unsigned32OrMinusZero())) {
76         return Truncation::Any(identify_zeros);
77       }
78       return Truncation(Truncation::TruncationKind::kWord32, identify_zeros);
79     }
80     case Truncation::TruncationKind::kWord64: {
81       if (type.Is(Type::BigInt())) {
82         DCHECK_EQ(identify_zeros, IdentifyZeros::kDistinguishZeros);
83         if (type.Is(Type::SignedBigInt64()) ||
84             type.Is(Type::UnsignedBigInt64())) {
85           return Truncation::Any(IdentifyZeros::kDistinguishZeros);
86         }
87       } else if (type.Is(TypeCache::Get()->kSafeIntegerOrMinusZero)) {
88         return Truncation::Any(identify_zeros);
89       }
90       return Truncation(Truncation::TruncationKind::kWord64, identify_zeros);
91     }
92 
93     default:
94       // TODO(nicohartmann): Support remaining truncations.
95       UNREACHABLE();
96   }
97 }
98 
VisitNode(Node * node,OperationTyper & op_typer)99 void SimplifiedLoweringVerifier::VisitNode(Node* node,
100                                            OperationTyper& op_typer) {
101   switch (node->opcode()) {
102     case IrOpcode::kInt64Constant: {
103       // Constants might be untyped, because they are cached in the graph and
104       // used in different contexts such that no single type can be assigned.
105       // Their type is provided by an introduced TypeGuard where necessary.
106       break;
107     }
108     case IrOpcode::kCheckedFloat64ToInt32: {
109       Type input_type = InputType(node, 0);
110       DCHECK(input_type.Is(Type::Number()));
111 
112       const auto& p = CheckMinusZeroParametersOf(node->op());
113       if (p.mode() == CheckForMinusZeroMode::kCheckForMinusZero) {
114         // Remove -0 from input_type.
115         input_type =
116             Type::Intersect(input_type, Type::Signed32(), graph_zone());
117       } else {
118         input_type = Type::Intersect(input_type, Type::Signed32OrMinusZero(),
119                                      graph_zone());
120       }
121       CheckAndSet(node, input_type, Truncation::Word32());
122       break;
123     }
124     case IrOpcode::kInt32Add: {
125       Type output_type =
126           op_typer.NumberAdd(InputType(node, 0), InputType(node, 1));
127       Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
128                                                        InputTruncation(node, 1),
129                                                        Truncation::Word32());
130       CHECK(IsModuloTruncation(output_trunc));
131       CheckAndSet(node, output_type, output_trunc);
132       break;
133     }
134     case IrOpcode::kInt32Sub: {
135       Type output_type =
136           op_typer.NumberSubtract(InputType(node, 0), InputType(node, 1));
137       Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
138                                                        InputTruncation(node, 1),
139                                                        Truncation::Word32());
140       CHECK(IsModuloTruncation(output_trunc));
141       CheckAndSet(node, output_type, output_trunc);
142       break;
143     }
144     case IrOpcode::kChangeInt31ToTaggedSigned: {
145       // ChangeInt31ToTaggedSigned is not truncating any values, so we can
146       // simply forward input.
147       CheckAndSet(node, InputType(node, 0), InputTruncation(node, 0));
148       break;
149     }
150     case IrOpcode::kChangeInt32ToTagged: {
151       // ChangeInt32ToTagged is not truncating any values, so we can simply
152       // forward input.
153       CheckAndSet(node, InputType(node, 0), InputTruncation(node, 0));
154       break;
155     }
156     case IrOpcode::kInt64Add: {
157       Type left_type = InputType(node, 0);
158       Type right_type = InputType(node, 1);
159 
160       Type output_type;
161       if (left_type.Is(Type::BigInt()) && right_type.Is(Type::BigInt())) {
162         // BigInt x BigInt -> BigInt
163         output_type = op_typer.BigIntAdd(left_type, right_type);
164       } else if (left_type.Is(Type::Number()) &&
165                  right_type.Is(Type::Number())) {
166         // Number x Number -> Number
167         output_type = op_typer.NumberAdd(left_type, right_type);
168       } else {
169         // Invalid type combination.
170         std::ostringstream left_str, right_str;
171         left_type.PrintTo(left_str);
172         right_type.PrintTo(right_str);
173         FATAL(
174             "SimplifiedLoweringVerifierError: invalid combination of input "
175             "types "
176             "%s and %s for node #%d:%s",
177             left_str.str().c_str(), right_str.str().c_str(), node->id(),
178             node->op()->mnemonic());
179       }
180 
181       Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
182                                                        InputTruncation(node, 1),
183                                                        Truncation::Word64());
184       CHECK(IsModuloTruncation(output_trunc));
185       CheckAndSet(node, output_type, output_trunc);
186       break;
187     }
188     case IrOpcode::kChangeInt32ToInt64: {
189       // ChangeInt32ToInt64 is not truncating any values, so we can simply
190       // forward input.
191       CheckAndSet(node, InputType(node, 0), InputTruncation(node, 0));
192       break;
193     }
194     case IrOpcode::kDeadValue: {
195       CheckAndSet(node, Type::None(), Truncation::Any());
196       break;
197     }
198     case IrOpcode::kTypeGuard: {
199       Type output_type = op_typer.TypeTypeGuard(node->op(), InputType(node, 0));
200       // TypeGuard has no effect on trunction, but the restricted type may help
201       // generalize it.
202       CheckAndSet(node, output_type, InputTruncation(node, 0));
203       break;
204     }
205     case IrOpcode::kTruncateBigIntToWord64: {
206       Type input_type = InputType(node, 0);
207       CHECK(input_type.Is(Type::BigInt()));
208       CHECK(Truncation::Word64().IsLessGeneralThan(InputTruncation(node, 0)));
209       CheckAndSet(node, input_type, Truncation::Word64());
210       break;
211     }
212     case IrOpcode::kChangeTaggedSignedToInt64: {
213       Type input_type = InputType(node, 0);
214       CHECK(input_type.Is(Type::Number()));
215       Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
216                                                        Truncation::Word64());
217       CheckAndSet(node, input_type, output_trunc);
218       break;
219     }
220     case IrOpcode::kCheckBigInt: {
221       Type input_type = InputType(node, 0);
222       input_type = Type::Intersect(input_type, Type::BigInt(), graph_zone());
223       CheckAndSet(node, input_type, InputTruncation(node, 0));
224       break;
225     }
226     case IrOpcode::kReturn: {
227       const int return_value_count = ValueInputCountOfReturn(node->op());
228       for (int i = 0; i < return_value_count; ++i) {
229         Type input_type = InputType(node, 1 + i);
230         Truncation input_trunc = InputTruncation(node, 1 + i);
231         input_trunc = GeneralizeTruncation(input_trunc, input_type);
232         // No values must be lost due to truncation.
233         CHECK_EQ(input_trunc, Truncation::Any());
234       }
235       break;
236     }
237     case IrOpcode::kSLVerifierHint: {
238       Type output_type = InputType(node, 0);
239       Truncation output_trunc = InputTruncation(node, 0);
240       const auto& p = SLVerifierHintParametersOf(node->op());
241 
242       if (const Operator* semantics = p.semantics()) {
243         switch (semantics->opcode()) {
244           case IrOpcode::kPlainPrimitiveToNumber:
245             output_type = op_typer.ToNumber(output_type);
246             break;
247           default:
248             UNREACHABLE();
249         }
250         CheckType(node, output_type);
251       }
252 
253       if (p.override_output_type()) {
254         output_type = *p.override_output_type();
255       }
256 
257       SetType(node, output_type);
258       SetTruncation(node, GeneralizeTruncation(output_trunc, output_type));
259       break;
260     }
261 
262     default:
263       // TODO(nicohartmann): Support remaining operators.
264       break;
265   }
266 }
267 
268 }  // namespace compiler
269 }  // namespace internal
270 }  // namespace v8
271