lang: Lower linear references
68c6e83abda6caf4a6c956a68c8ec9cc7ff7c940329607cb52aff9099db47ee9
1 parent
d45ecf68
lib/std/lang/lower.rad
+75 -46
| 3255 | 3255 | set payload = tvalPayloadAddr(self, base, valOffset), |
|
| 3256 | 3256 | }; |
|
| 3257 | 3257 | return newVar(self, name, ilType(self.low, bindType), mutable, payload); |
|
| 3258 | 3258 | } |
|
| 3259 | 3259 | ||
| 3260 | + | /// Bind an identifier from a matched subject. |
|
| 3260 | 3261 | fn bindMatchVariable( |
|
| 3261 | 3262 | self: *mut FnLowerer, |
|
| 3262 | 3263 | subject: *MatchSubject, |
|
| 3263 | 3264 | binding: *ast::Node, |
|
| 3264 | 3265 | mutable: bool |
| 3356 | 3357 | }; |
|
| 3357 | 3358 | try bindFieldVariable(self, elem, base, fieldInfo, subject.by, failBlock); |
|
| 3358 | 3359 | } |
|
| 3359 | 3360 | } |
|
| 3360 | 3361 | ||
| 3362 | + | /// Bind fields from a record pattern. |
|
| 3361 | 3363 | fn bindRecordPatternFields(self: *mut FnLowerer, subject: *MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) { |
|
| 3362 | 3364 | // No fields to bind (e.g., `{ .. }`). |
|
| 3363 | 3365 | if lit.fields.len == 0 { |
|
| 3364 | 3366 | return; |
|
| 3365 | 3367 | } |
| 3855 | 3857 | } |
|
| 3856 | 3858 | ||
| 3857 | 3859 | /// Lower a `let-else` statement. |
|
| 3858 | 3860 | fn lowerLetElse(self: *mut FnLowerer, letElse: ast::LetElse) throws (LowerError) { |
|
| 3859 | 3861 | let subject = try lowerMatchSubject(self, letElse.pattern.scrutinee); |
|
| 3860 | - | let mut mergeBlock: BlockId = undefined; |
|
| 3862 | + | let mut successBlock: BlockId = undefined; |
|
| 3861 | 3863 | if letElse.pattern.guard == nil { |
|
| 3862 | - | set mergeBlock = try createBlock(self, "merge"); |
|
| 3864 | + | set successBlock = try createBlock(self, "success"); |
|
| 3863 | 3865 | } |
|
| 3864 | - | // Else branch executes when the pattern fails to match. |
|
| 3866 | + | // The else branch executes when the pattern fails to match. |
|
| 3865 | 3867 | let elseBlock = try createBlock(self, "else"); |
|
| 3866 | 3868 | ||
| 3867 | - | // Evaluate pattern and jump to @end or @else. |
|
| 3868 | - | try lowerPatternMatch(self, &subject, &letElse.pattern, &mut mergeBlock, "merge", elseBlock); |
|
| 3869 | + | // Evaluate the pattern and jump to @success or @else. |
|
| 3870 | + | try lowerPatternMatch( |
|
| 3871 | + | self, |
|
| 3872 | + | &subject, |
|
| 3873 | + | &letElse.pattern, |
|
| 3874 | + | &mut successBlock, |
|
| 3875 | + | "success", |
|
| 3876 | + | elseBlock, |
|
| 3877 | + | ); |
|
| 3878 | + | let mut bindingVar: ?Var = nil; |
|
| 3879 | + | if let case ast::PatternKind::Binding = letElse.pattern.kind { |
|
| 3880 | + | set bindingVar = lookupLocalVar(self, letElse.pattern.pattern); |
|
| 3881 | + | } |
|
| 3882 | + | let mergeBlock = try createBlock(self, "merge"); |
|
| 3883 | + | try emitJmp(self, mergeBlock); |
|
| 3884 | + | ||
| 3869 | 3885 | try switchToAndSeal(self, elseBlock); |
|
| 3870 | - | try lowerNode(self, letElse.elseBranch); |
|
| 3886 | + | if try typeOf(self, letElse.elseBranch) == resolver::Type::Never { |
|
| 3887 | + | try lowerNode(self, letElse.elseBranch); |
|
| 3888 | + | } else { |
|
| 3889 | + | let fallback = try lowerExpr(self, letElse.elseBranch); |
|
| 3890 | + | if let variable = bindingVar { |
|
| 3891 | + | defVar(self, variable, fallback); |
|
| 3892 | + | } |
|
| 3893 | + | try emitJmp(self, mergeBlock); |
|
| 3894 | + | } |
|
| 3871 | 3895 | ||
| 3872 | - | // Continue in @merge. The else branch must diverge, so @merge has only |
|
| 3873 | - | // one predecessor. |
|
| 3896 | + | // Continue at @merge after a successful match or value-producing fallback. |
|
| 3874 | 3897 | try switchToAndSeal(self, mergeBlock); |
|
| 3875 | 3898 | } |
|
| 3876 | 3899 | ||
| 3877 | 3900 | /// Lower a `while let` loop as a match-driven loop. |
|
| 3878 | 3901 | fn lowerWhileLet(self: *mut FnLowerer, w: ast::WhileLet) throws (LowerError) { |
| 4050 | 4073 | } |
|
| 4051 | 4074 | ||
| 4052 | 4075 | /// Check if a resolver type lowers to an aggregate in memory. |
|
| 4053 | 4076 | fn isAggregateType(typ: resolver::Type) -> bool { |
|
| 4054 | 4077 | match typ { |
|
| 4055 | - | case resolver::Type::Nominal(_) => { |
|
| 4056 | - | // Void unions are small enough to pass by value. |
|
| 4057 | - | return not resolver::isVoidUnion(typ); |
|
| 4058 | - | } |
|
| 4059 | 4078 | case resolver::Type::Slice { .. }, |
|
| 4060 | - | resolver::Type::TraitObject { .. }, |
|
| 4061 | - | resolver::Type::Array(_), |
|
| 4062 | - | resolver::Type::Nil => return true, |
|
| 4079 | + | resolver::Type::TraitObject { .. } => return true, |
|
| 4063 | 4080 | case resolver::Type::Optional(resolver::Type::Pointer { .. }) => { |
|
| 4064 | 4081 | // Optional pointers are scalar due to NPO. |
|
| 4065 | 4082 | return false; |
|
| 4066 | 4083 | } |
|
| 4067 | 4084 | case resolver::Type::Optional(_) => { |
|
| 4068 | 4085 | // All other optionals, including optional slices, are aggregates. |
|
| 4069 | 4086 | return true; |
|
| 4070 | 4087 | } |
|
| 4088 | + | case resolver::Type::Nominal(_) => { |
|
| 4089 | + | // Void unions are small enough to pass by value. |
|
| 4090 | + | return not resolver::isVoidUnion(typ); |
|
| 4091 | + | } |
|
| 4092 | + | case resolver::Type::Array(_), |
|
| 4093 | + | resolver::Type::Nil => return true, |
|
| 4071 | 4094 | else => return false, |
|
| 4072 | 4095 | } |
|
| 4073 | 4096 | } |
|
| 4074 | 4097 | ||
| 4075 | 4098 | /// Check if a resolver type is a small aggregate that can be |
| 4217 | 4240 | /// Build a `nil` value for an optional type. |
|
| 4218 | 4241 | /// |
|
| 4219 | 4242 | /// For optional pointers (`?*T`), returns an immediate `0` (null pointer). |
|
| 4220 | 4243 | /// For other optionals, builds a tagged aggregate with tag set to `0` (absent). |
|
| 4221 | 4244 | fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4222 | - | match optType { |
|
| 4223 | - | case resolver::Type::Optional(resolver::Type::Pointer { .. }) => { |
|
| 4224 | - | return il::Val::Imm(0); |
|
| 4225 | - | } |
|
| 4226 | - | case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => { |
|
| 4227 | - | return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)); |
|
| 4228 | - | } |
|
| 4229 | - | case resolver::Type::Optional(inner) => { |
|
| 4230 | - | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4231 | - | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
|
| 4232 | - | } |
|
| 4233 | - | else => throw LowerError::ExpectedOptional, |
|
| 4245 | + | let case resolver::Type::Optional(inner) = optType |
|
| 4246 | + | else throw LowerError::ExpectedOptional; |
|
| 4247 | + | if let case resolver::Type::Pointer { .. } = *inner { |
|
| 4248 | + | return il::Val::Imm(0); |
|
| 4249 | + | } |
|
| 4250 | + | if let case resolver::Type::Slice { item, mutable, .. } = *inner { |
|
| 4251 | + | return try buildSliceValue( |
|
| 4252 | + | self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0) |
|
| 4253 | + | ); |
|
| 4234 | 4254 | } |
|
| 4255 | + | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4256 | + | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
|
| 4235 | 4257 | } |
|
| 4236 | 4258 | ||
| 4237 | 4259 | /// Build a result value for throwing functions. |
|
| 4238 | 4260 | fn buildResult( |
|
| 4239 | 4261 | self: *mut FnLowerer, |
| 4256 | 4278 | ptrVal: il::Val, |
|
| 4257 | 4279 | lenVal: il::Val, |
|
| 4258 | 4280 | capVal: il::Val |
|
| 4259 | 4281 | ) -> il::Val throws (LowerError) { |
|
| 4260 | 4282 | let sliceType = resolver::Type::Slice { |
|
| 4261 | - | class: types::PointerClass::Owned, item: elemTy, mutable, |
|
| 4283 | + | class: types::PointerClass::Unsafe, |
|
| 4284 | + | item: elemTy, |
|
| 4285 | + | mutable, |
|
| 4262 | 4286 | }; |
|
| 4263 | 4287 | let dst = try emitReserve(self, sliceType); |
|
| 4264 | 4288 | let ptrTy = resolver::Type::Pointer { |
|
| 4265 | - | class: types::PointerClass::Owned, target: elemTy, mutable, |
|
| 4289 | + | class: types::PointerClass::Unsafe, |
|
| 4290 | + | target: elemTy, |
|
| 4291 | + | mutable, |
|
| 4266 | 4292 | }; |
|
| 4267 | 4293 | ||
| 4268 | 4294 | try emitStore(self, dst, SLICE_PTR_OFFSET, ptrTy, ptrVal); |
|
| 4269 | 4295 | try emitStore(self, dst, SLICE_LEN_OFFSET, resolver::Type::U32, lenVal); |
|
| 4270 | 4296 | try emitStore(self, dst, SLICE_CAP_OFFSET, resolver::Type::U32, capVal); |
| 4450 | 4476 | a: il::Reg, |
|
| 4451 | 4477 | b: il::Reg, |
|
| 4452 | 4478 | offset: i32 |
|
| 4453 | 4479 | ) -> il::Val throws (LowerError) { |
|
| 4454 | 4480 | let ptrTy = resolver::Type::Pointer { |
|
| 4455 | - | class: types::PointerClass::Owned, target: elemTy, mutable, |
|
| 4481 | + | class: types::PointerClass::Unsafe, |
|
| 4482 | + | target: elemTy, |
|
| 4483 | + | mutable, |
|
| 4456 | 4484 | }; |
|
| 4457 | 4485 | let ptrEq = try emitEqAtOffset(self, a, b, offset + SLICE_PTR_OFFSET, ptrTy); |
|
| 4458 | 4486 | let lenEq = try emitEqAtOffset(self, a, b, offset + SLICE_LEN_OFFSET, resolver::Type::U32); |
|
| 4459 | 4487 | ||
| 4460 | 4488 | return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, ptrEq, lenEq); |
| 4700 | 4728 | a: il::Reg, |
|
| 4701 | 4729 | b: il::Reg, |
|
| 4702 | 4730 | offset: i32 |
|
| 4703 | 4731 | ) -> il::Val throws (LowerError) { |
|
| 4704 | 4732 | match typ { |
|
| 4705 | - | case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => { |
|
| 4706 | - | // Optional slices use null pointer optimization. |
|
| 4707 | - | return try lowerSliceEq(self, item, mutable, a, b, offset); |
|
| 4708 | - | } |
|
| 4733 | + | case resolver::Type::Slice { item, mutable, .. } => |
|
| 4734 | + | return try lowerSliceEq(self, item, mutable, a, b, offset), |
|
| 4709 | 4735 | case resolver::Type::Optional(inner) => { |
|
| 4736 | + | if let case resolver::Type::Slice { item, mutable, .. } = *inner { |
|
| 4737 | + | // Optional slices use null pointer optimization. |
|
| 4738 | + | return try lowerSliceEq(self, item, mutable, a, b, offset); |
|
| 4739 | + | } |
|
| 4710 | 4740 | return try lowerOptionalEq(self, *inner, a, b, offset); |
|
| 4711 | 4741 | } |
|
| 4712 | - | case resolver::Type::Slice { item, mutable, .. } => |
|
| 4713 | - | return try lowerSliceEq(self, item, mutable, a, b, offset), |
|
| 4714 | 4742 | case resolver::Type::Array(arr) => |
|
| 4715 | 4743 | return try lowerArrayEq(self, arr, a, b, offset), |
|
| 4716 | 4744 | case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => |
|
| 4717 | 4745 | return try lowerRecordEq(self, recInfo, a, b, offset), |
|
| 4718 | 4746 | case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) => |
| 5090 | 5118 | else => throw LowerError::UnexpectedNodeValue(arrayNode), |
|
| 5091 | 5119 | } |
|
| 5092 | 5120 | let result = dataBuilderFinish(&b); |
|
| 5093 | 5121 | let alignment = resolver::getTypeLayout(*item).alignment; |
|
| 5094 | 5122 | return try lowerConstDataAsSlice( |
|
| 5095 | - | self, result.values, alignment, not mutable, item, mutable, length |
|
| 5123 | + | self, result.values, alignment, not mutable, |
|
| 5124 | + | item, mutable, length |
|
| 5096 | 5125 | ); |
|
| 5097 | 5126 | } |
|
| 5098 | 5127 | let data = try lowerExpr(self, arrayNode); |
|
| 5099 | 5128 | let count = il::Val::Imm(length as i64); |
|
| 5100 | 5129 | return try buildSliceValue(self, item, mutable, data, count, count); |
| 6147 | 6176 | /// String literals are stored as global data and the result is a slice |
|
| 6148 | 6177 | /// pointing to the data with the appropriate length. |
|
| 6149 | 6178 | fn lowerStringLit(self: *mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) { |
|
| 6150 | 6179 | // Get the slice type from the node. |
|
| 6151 | 6180 | let sliceTy = try typeOf(self, node); |
|
| 6152 | - | let case resolver::Type::Slice { item, mutable, .. } = sliceTy else { |
|
| 6153 | - | throw LowerError::ExpectedSliceOrArray; |
|
| 6154 | - | }; |
|
| 6181 | + | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
|
| 6182 | + | else throw LowerError::ExpectedSliceOrArray; |
|
| 6155 | 6183 | // Build the string data value. |
|
| 6156 | 6184 | let ptr = try! alloc::alloc( |
|
| 6157 | 6185 | self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue) |
|
| 6158 | 6186 | ) as *mut il::DataValue; |
|
| 6159 | 6187 | ||
| 6160 | 6188 | set *ptr = il::DataValue { item: il::DataItem::Str(s), count: 1 }; |
|
| 6161 | 6189 | ||
| 6162 | - | return try lowerConstDataAsSlice(self, @sliceOf(ptr, 1), 1, true, item, mutable, s.len); |
|
| 6190 | + | return try lowerConstDataAsSlice( |
|
| 6191 | + | self, @sliceOf(ptr, 1), 1, true, item, mutable, s.len |
|
| 6192 | + | ); |
|
| 6163 | 6193 | } |
|
| 6164 | 6194 | ||
| 6165 | 6195 | /// Lower a builtin call expression. |
|
| 6166 | 6196 | fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6167 | 6197 | match kind { |
| 6179 | 6209 | fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6180 | 6210 | if args.len <> 2 and args.len <> 3 { |
|
| 6181 | 6211 | throw LowerError::InvalidArgCount; |
|
| 6182 | 6212 | } |
|
| 6183 | 6213 | let sliceTy = try typeOf(self, node); |
|
| 6184 | - | let case resolver::Type::Slice { item, mutable, .. } = sliceTy else { |
|
| 6185 | - | throw LowerError::ExpectedSliceOrArray; |
|
| 6186 | - | }; |
|
| 6214 | + | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
|
| 6215 | + | else throw LowerError::ExpectedSliceOrArray; |
|
| 6187 | 6216 | let ptrVal = try lowerExpr(self, args[0]); |
|
| 6188 | 6217 | let lenVal = try lowerExpr(self, args[1]); |
|
| 6189 | 6218 | let mut capVal = lenVal; |
|
| 6190 | 6219 | if args.len == 3 { |
|
| 6191 | 6220 | set capVal = try lowerExpr(self, args[2]); |
| 7287 | 7316 | resolver::Type::U32 => return il::Type::W32, |
|
| 7288 | 7317 | case resolver::Type::I64, |
|
| 7289 | 7318 | resolver::Type::U64, |
|
| 7290 | 7319 | resolver::Type::Pointer { .. }, |
|
| 7291 | 7320 | resolver::Type::Slice { .. }, |
|
| 7321 | + | resolver::Type::TraitObject { .. }, |
|
| 7292 | 7322 | resolver::Type::Array(_), |
|
| 7293 | 7323 | resolver::Type::Optional(_), |
|
| 7294 | - | resolver::Type::Fn(_), |
|
| 7295 | - | resolver::Type::TraitObject { .. } => return il::Type::W64, |
|
| 7324 | + | resolver::Type::Fn(_) => return il::Type::W64, |
|
| 7296 | 7325 | case resolver::Type::Nominal(_) => { |
|
| 7297 | 7326 | if resolver::isVoidUnion(typ) { |
|
| 7298 | 7327 | return il::Type::W8; |
|
| 7299 | 7328 | } |
|
| 7300 | 7329 | return il::Type::W64; |
test/tests/coercion.implicit.ril
+10 -6
| 4 | 4 | store w8 1 %0 0; |
|
| 5 | 5 | store w32 42 %0 4; |
|
| 6 | 6 | reserve %1 8 4; |
|
| 7 | 7 | blit %1 %0 8; |
|
| 8 | 8 | load w8 %2 %1 0; |
|
| 9 | - | br.ne w32 %2 0 @merge1 @else2; |
|
| 10 | - | @merge1 |
|
| 9 | + | br.ne w32 %2 0 @success1 @else2; |
|
| 10 | + | @success1 |
|
| 11 | 11 | sload w32 %3 %1 4; |
|
| 12 | - | ret %3; |
|
| 12 | + | jmp @merge3; |
|
| 13 | 13 | @else2 |
|
| 14 | 14 | ret 0; |
|
| 15 | + | @merge3 |
|
| 16 | + | ret %3; |
|
| 15 | 17 | } |
|
| 16 | 18 | ||
| 17 | 19 | fn w32 $optionalLiftVar(w32 %0) { |
|
| 18 | 20 | @entry0 |
|
| 19 | 21 | reserve %1 8 4; |
| 22 | 24 | reserve %2 8 4; |
|
| 23 | 25 | blit %2 %1 8; |
|
| 24 | 26 | reserve %3 8 4; |
|
| 25 | 27 | blit %3 %2 8; |
|
| 26 | 28 | load w8 %4 %3 0; |
|
| 27 | - | br.ne w32 %4 0 @merge1 @else2; |
|
| 28 | - | @merge1 |
|
| 29 | + | br.ne w32 %4 0 @success1 @else2; |
|
| 30 | + | @success1 |
|
| 29 | 31 | sload w32 %5 %3 4; |
|
| 30 | - | ret %5; |
|
| 32 | + | jmp @merge3; |
|
| 31 | 33 | @else2 |
|
| 32 | 34 | ret 0; |
|
| 35 | + | @merge3 |
|
| 36 | + | ret %5; |
|
| 33 | 37 | } |
|
| 34 | 38 | ||
| 35 | 39 | fn w64 $optionalLiftReturn(w64 %0, w32 %1) { |
|
| 36 | 40 | @entry0 |
|
| 37 | 41 | reserve %2 8 4; |
test/tests/cond.letelse.case.ril
+5 -3
| 1 | 1 | fn w32 $letElseCase(w32 %0) { |
|
| 2 | 2 | @entry0 |
|
| 3 | - | br.eq w32 %0 1 @merge1 @else2; |
|
| 4 | - | @merge1 |
|
| 5 | - | ret 1; |
|
| 3 | + | br.eq w32 %0 1 @success1 @else2; |
|
| 4 | + | @success1 |
|
| 5 | + | jmp @merge3; |
|
| 6 | 6 | @else2 |
|
| 7 | 7 | ret 0; |
|
| 8 | + | @merge3 |
|
| 9 | + | ret 1; |
|
| 8 | 10 | } |
test/tests/cond.letelse.guard.ril
+4 -2
| 2 | 2 | @entry0 |
|
| 3 | 3 | br.eq w32 %0 1 @guard2 @else1; |
|
| 4 | 4 | @else1 |
|
| 5 | 5 | ret 0; |
|
| 6 | 6 | @guard2 |
|
| 7 | - | br.slt w32 0 %0 @merge3 @else1; |
|
| 8 | - | @merge3 |
|
| 7 | + | br.slt w32 0 %0 @success3 @else1; |
|
| 8 | + | @success3 |
|
| 9 | + | jmp @merge4; |
|
| 10 | + | @merge4 |
|
| 9 | 11 | ret 1; |
|
| 10 | 12 | } |
test/tests/cond.letelse.mut.ril
+6 -4
| 1 | 1 | fn w32 $letMutElse(w64 %0) { |
|
| 2 | 2 | @entry0 |
|
| 3 | 3 | reserve %1 8 4; |
|
| 4 | 4 | blit %1 %0 8; |
|
| 5 | 5 | load w8 %2 %1 0; |
|
| 6 | - | br.ne w32 %2 0 @merge1 @else2; |
|
| 7 | - | @merge1 |
|
| 6 | + | br.ne w32 %2 0 @success1 @else2; |
|
| 7 | + | @success1 |
|
| 8 | 8 | sload w32 %3 %1 4; |
|
| 9 | - | add w32 %4 %3 1; |
|
| 10 | - | ret %4; |
|
| 9 | + | jmp @merge3; |
|
| 11 | 10 | @else2 |
|
| 12 | 11 | ret 0; |
|
| 12 | + | @merge3 |
|
| 13 | + | add w32 %4 %3 1; |
|
| 14 | + | ret %4; |
|
| 13 | 15 | } |
test/tests/cond.letelse.optional.ril
+5 -3
| 1 | 1 | fn w32 $letElseOptional(w64 %0) { |
|
| 2 | 2 | @entry0 |
|
| 3 | - | br.ne w32 %0 0 @merge1 @else2; |
|
| 4 | - | @merge1 |
|
| 5 | - | ret 1; |
|
| 3 | + | br.ne w32 %0 0 @success1 @else2; |
|
| 4 | + | @success1 |
|
| 5 | + | jmp @merge3; |
|
| 6 | 6 | @else2 |
|
| 7 | 7 | ret 0; |
|
| 8 | + | @merge3 |
|
| 9 | + | ret 1; |
|
| 8 | 10 | } |
test/tests/opt.slice.npo.ril
+8 -7
| 135 | 135 | reserve %2 16 8; |
|
| 136 | 136 | blit %2 %1 16; |
|
| 137 | 137 | reserve %3 16 8; |
|
| 138 | 138 | blit %3 %2 16; |
|
| 139 | 139 | load w64 %4 %3 0; |
|
| 140 | - | br.ne w32 %4 0 @merge1 @else2; |
|
| 141 | - | @merge1 |
|
| 142 | - | load w32 %5 %3 8; |
|
| 143 | - | br.eq w32 %5 3 @assert.ok4 @assert.fail3; |
|
| 140 | + | br.ne w32 %4 0 @success1 @else2; |
|
| 141 | + | @success1 |
|
| 142 | + | jmp @merge3; |
|
| 144 | 143 | @else2 |
|
| 145 | 144 | ret 40; |
|
| 146 | - | @assert.fail3 |
|
| 145 | + | @merge3 |
|
| 146 | + | load w32 %5 %3 8; |
|
| 147 | + | br.eq w32 %5 3 @assert.ok5 @assert.fail4; |
|
| 148 | + | @assert.fail4 |
|
| 147 | 149 | unreachable; |
|
| 148 | - | @assert.ok4 |
|
| 150 | + | @assert.ok5 |
|
| 149 | 151 | ret 0; |
|
| 150 | 152 | } |
|
| 151 | 153 | ||
| 152 | 154 | fn w64 $returnNil(w64 %0) { |
|
| 153 | 155 | @entry0 |
| 358 | 360 | @then15 |
|
| 359 | 361 | ret %7; |
|
| 360 | 362 | @merge16 |
|
| 361 | 363 | ret 0; |
|
| 362 | 364 | } |
|
| 363 | - |