Files
swift-mirror/test/SILOptimizer/closure_specialization_simple.sil
Erik Eckstein df20d36255 ClosureSpecialization: support for OSSA and a big overhaul
Beside supporting OSSA, this change significantly simplifies the pass.
The main change is that instead of starting at a closure (e.g. `partial_apply`) and finding all call sites, we now start at a call site and look for closures for all arguments. This makes a lot of things much simpler, e.g. not so many intermediate data structures are required to track all the states.

I needed to remove the 3 unit tests because the things those tests were testing are not there anymore. However, the pass is tested with a lot of sil tests (and I added quite a few), which should give good test coverage.

The old ClosureSpecializer pass is still kept in place, because at that point in the pipeline we don't have OSSA, yet. Once we have that, we can replace the old pass withe the new one.
However, the autodiff closure specializer already runs in the OSSA pipeline and there the new changes take effect.
2025-10-06 12:02:48 +02:00

388 lines
18 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all -closure-specialization %s | %FileCheck %s
import Builtin
import Swift
sil @simple_partial_apply_fun : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK-LABEL: sil shared [ossa] @$s27simple_partial_apply_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1 {
// CHECK: bb0([[CAPTURED_ARG:%.*]] : $Builtin.Int1):
// CHECK: [[CLOSED_OVER_FUN:%.*]] = function_ref @simple_partial_apply_fun :
// CHECK: [[NEW_PAI:%.*]] = partial_apply [[CLOSED_OVER_FUN]]
// CHECK: destroy_value [[NEW_PAI]]
sil [ossa] @simple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
bb0(%0 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1):
br bb1
bb1:
%1 = integer_literal $Builtin.Int1, 0
// We cannot do anything here for now but in the future I think we should try
// to handle this in closure specialization potentially.
%0c = copy_value %0
%2 = apply %0c(%1) : $@callee_owned (Builtin.Int1) -> Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
return %2 : $Builtin.Int1
}
// CHECK-LABEL: sil shared [ossa] @$s37simple_partial_apply_2nd_level_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1 {
// CHECK: bb0([[CAPTURED_ARG:%.*]] : $Builtin.Int1):
// CHECK: [[SPECIALIZED_CALLEE:%.*]] = function_ref @$s27simple_partial_apply_caller0a1_b1_C4_funBi1_Tf1c_n :
// CHECK: [[RET:%.*]]= apply [[SPECIALIZED_CALLEE]]([[CAPTURED_ARG]])
// CHECK: return [[RET]]
sil [ossa] @simple_partial_apply_2nd_level_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
bb0(%0 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1):
br bb1
bb1:
%1 = function_ref @simple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%0c = copy_value %0
%2 = apply %1(%0c) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
return %2 : $Builtin.Int1
}
sil @simple_partial_apply_caller_decl : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
// CHECK-LABEL: sil shared [ossa] @$s36simple_multiple_partial_apply_caller0a1_c1_D4_funBi1_ABBi1_Tf1cc_n : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1 {
// CHECK: [[C:%.*]] = function_ref @simple_partial_apply_fun : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: = partial_apply [[C]](%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: = partial_apply [[C]](%1) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: } // end sil function '$s36simple_multiple_partial_apply_caller0a1_c1_D4_funBi1_ABBi1_Tf1cc_n'
// CHECK-LABEL: sil shared [ossa] @$s36simple_multiple_partial_apply_caller19closure_with_stringSSTf1cC0_n : $@convention(thin) (@owned String) -> Builtin.Int1 {
// %0 // user: %2
// CHECK: %1 = function_ref @closure_with_string : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
// CHECK: %2 = partial_apply %1(%0) : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
// CHECK: %3 = copy_value %2
// CHECK: br bb1
// CHECK: } // end sil function '$s36simple_multiple_partial_apply_caller19closure_with_stringSSTf1cC0_n'
sil [ossa] @simple_multiple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
bb0(%0 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1, %1 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1):
br bb1
bb1:
%2 = integer_literal $Builtin.Int1, 0
// We cannot do anything here for now but in the future I think we should try
// to handle this in closure specialization potentially.
%0c = copy_value %0
apply %0c(%2) : $@callee_owned (Builtin.Int1) -> Builtin.Int1
%1c = copy_value %1
apply %1c(%2) : $@callee_owned (Builtin.Int1) -> Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
destroy_value %1
return %2 : $Builtin.Int1
}
sil @simple_partial_apply_fun2 : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
sil [ossa] @simple_partial_apply_caller2 : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
bb0(%0 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1):
br bb1
bb1:
%1 = integer_literal $Builtin.Int1, 0
// We cannot do anything here for now but in the future I think we should try
// to handle this in closure specialization potentially.
%0c = copy_value %0
%2 = apply %0c(%1) : $@callee_owned (Builtin.Int1) -> Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
return %2 : $Builtin.Int1
}
sil @indirect_parameter_partial_apply_fun : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
sil [ossa] @indirect_parameter_partial_apply_caller1 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1) -> () {
bb0(%0 : @owned $@callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1):
br bb1
bb1:
%1 = alloc_stack $Builtin.Int1
%2 = alloc_stack $Builtin.Int1
%3 = alloc_stack $Builtin.Int1
%4 = integer_literal $Builtin.Int1, 0
store %4 to [trivial] %1
store %4 to [trivial] %2
%0c = copy_value %0
apply %0c(%3, %1, %4, %2) : $@callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
dealloc_stack %3
dealloc_stack %2
dealloc_stack %1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller2 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1) -> () {
bb0(%0 : @owned $@callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1):
br bb1
bb1:
%1 = alloc_stack $Builtin.Int1
%2 = integer_literal $Builtin.Int1, 0
%0c = copy_value %0
apply %0c(%1, %1, %2) : $@callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1
dealloc_stack %1 : $*Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller3 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1) -> @out Builtin.Int1) -> () {
bb0(%0 : @owned $@callee_owned (@in Builtin.Int1) -> @out Builtin.Int1):
br bb1
bb1:
%1 = alloc_stack $Builtin.Int1
%0c = copy_value %0
apply %0c(%1, %1) : $@callee_owned (@in Builtin.Int1) -> @out Builtin.Int1
dealloc_stack %1 : $*Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller4 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> () {
bb0(%0 : @owned $@callee_owned () -> @out Builtin.Int1):
br bb1
bb1:
%1 = alloc_stack $Builtin.Int1
%0c = copy_value %0
apply %0c(%1) : $@callee_owned () -> @out Builtin.Int1
dealloc_stack %1 : $*Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller5 : $@convention(thin) (@owned @callee_owned () -> ()) -> () {
bb0(%0 : @owned $@callee_owned () -> ()):
br bb1
bb1:
%0c = copy_value %0
apply %0c() : $@callee_owned () -> ()
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller6 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out Builtin.Int1 {
bb0(%1 : $*Builtin.Int1, %0 : @owned $@callee_owned () -> @out Builtin.Int1):
br bb1
bb1:
%0c = copy_value %0
apply %0c(%1) : $@callee_owned () -> @out Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller7 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out (Builtin.Int1, Builtin.Int1) {
bb0(%1 : $*(Builtin.Int1, Builtin.Int1), %0 : @owned $@callee_owned () -> @out Builtin.Int1):
br bb1
bb1:
%2 = alloc_stack $Builtin.Int1
%3 = alloc_stack $Builtin.Int1
%0c = copy_value %0
%0d = copy_value %0
apply %0c(%2) : $@callee_owned () -> @out Builtin.Int1
apply %0d(%3) : $@callee_owned () -> @out Builtin.Int1
%4 = load [trivial] %2: $*Builtin.Int1
%5 = load [trivial] %3: $*Builtin.Int1
%6 = tuple(%4 : $Builtin.Int1, %5: $Builtin.Int1)
store %6 to [trivial] %1 : $*(Builtin.Int1, Builtin.Int1)
dealloc_stack %3: $*Builtin.Int1
dealloc_stack %2: $*Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @loop_driver : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
// CHECK-DAG: [[SPECIALIZED_FUN:%.*]] = function_ref @$s27simple_partial_apply_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1
// CHECK: apply [[SPECIALIZED_FUN]]
// CHECK-DAG: [[SPECIALIZED_FUN2:%.*]] = function_ref @$s37simple_partial_apply_2nd_level_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1
// CHECK: apply [[SPECIALIZED_FUN2]]
// We can't call this one b/c it is just a declaration.
// CHECK: [[UNSPECIALIZED_FUN_DECL:%.*]] = function_ref @simple_partial_apply_caller_decl : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
// CHECK: apply [[UNSPECIALIZED_FUN_DECL]]
// We handle closures with indirect results.
// CHECK: [[CLOSUREFUN:%.*]] = function_ref @indirect_parameter_partial_apply_fun
// CHECK-NOT: partial_apply [[CLOSUREFUN]]()
// CHECK-NOT: partial_apply [[CLOSUREFUN]]()
// We don't handle captured indirect @in and @in_guaranteed parameters yet.
// CHECK: [[CLOSURE2:%.*]] = partial_apply [[CLOSUREFUN]](%{{.*}})
// CHECK: [[CLOSURE3:%.*]] = partial_apply [[CLOSUREFUN]](%{{.*}})
// CHECK: [[CLOSURE4:%.*]] = partial_apply [[CLOSUREFUN]](%{{.*}})
// CHECK: [[CALLER1:%.*]] = function_ref @indirect_parameter_partial_apply_caller1
// CHECK: [[CALLER2:%.*]] = function_ref @indirect_parameter_partial_apply_caller2
// CHECK: [[CALLER3:%.*]] = function_ref @indirect_parameter_partial_apply_caller3
// CHECK: [[CALLER4:%.*]] = function_ref @indirect_parameter_partial_apply_caller4
// Closure with indirect result but no captured indirect parameter.
// CHECK-NOT: apply [[CALLER1]]
// CHECK: [[INLINEDCLOSURE_CALLER1:%.*]] = function_ref @$s40indirect_parameter_partial_apply_caller10a1_b1_c1_D4_funTf1c_n
// CHECK: apply [[INLINEDCLOSURE_CALLER1]]()
// CHECK-NOT: apply [[CALLER1]]
// Closures with captured indirect parameters.
// CHECK: apply [[CALLER2]]([[CLOSURE2]])
// CHECK: apply [[CALLER3]]([[CLOSURE3]])
// CHECK: apply [[CALLER4]]([[CLOSURE4]])
// CHECK: [[SM:%.*]] = function_ref @$s36simple_multiple_partial_apply_caller0a1_c1_D4_funBi1_ABBi1_Tf1cc_n : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: apply [[SM]](%0, %0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: } // end sil function 'loop_driver'
sil [ossa] @loop_driver : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
%2 = function_ref @simple_partial_apply_fun : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%3 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%4 = function_ref @simple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%3c = copy_value %3
%5 = apply %4(%3c) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%51 = function_ref @simple_partial_apply_2nd_level_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%52 = apply %51(%3) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%6 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%7 = function_ref @simple_partial_apply_caller_decl : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%8 = apply %7(%6) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%9 = alloc_stack $Builtin.Int1
%10 = function_ref @indirect_parameter_partial_apply_fun : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
%11 = partial_apply %10() : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
%12 = partial_apply %10(%9) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
%13 = partial_apply %10(%1, %9) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
%14 = partial_apply %10(%9, %1, %9) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
%16 = function_ref @indirect_parameter_partial_apply_caller1 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1) -> ()
%17 = function_ref @indirect_parameter_partial_apply_caller2 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1) -> ()
%18 = function_ref @indirect_parameter_partial_apply_caller3 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1) -> @out Builtin.Int1) -> ()
%19 = function_ref @indirect_parameter_partial_apply_caller4 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> ()
%20 = function_ref @indirect_parameter_partial_apply_caller5 : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
apply %16(%11) : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1) -> ()
apply %17(%12) : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1) -> ()
apply %18(%13) : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1) -> @out Builtin.Int1) -> ()
apply %19(%14) : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> ()
// Make sure we handle when we already have an out parameter correctly.
%21 = alloc_stack $(Builtin.Int1, Builtin.Int1)
%22 = function_ref @indirect_parameter_partial_apply_caller6 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out Builtin.Int1
%23 = function_ref @indirect_parameter_partial_apply_caller7 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out (Builtin.Int1, Builtin.Int1)
%24 = partial_apply %10(%9, %1, %9) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
%25 = partial_apply %10(%9, %1, %9) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
apply %22(%9, %24) : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out Builtin.Int1
apply %23(%21, %25) : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out (Builtin.Int1, Builtin.Int1)
dealloc_stack %21 : $*(Builtin.Int1, Builtin.Int1)
dealloc_stack %9 : $*Builtin.Int1
%26 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%27 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%28 = function_ref @simple_multiple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%29 = apply %28(%26, %27) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%30 = function_ref @simple_partial_apply_fun2 : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%31 = partial_apply %30(%1) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%32 = function_ref @simple_partial_apply_caller2 : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%33 = apply %32(%31) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%9999 = tuple()
return %9999 : $()
}
sil @closure_with_string : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
// CHECK-LABEL: sil [ossa] @test_multi_closure_with_string :
// CHECK: [[C:%.*]] = copy_value %0
// CHECK: [[F:%.*]] = function_ref @$s36simple_multiple_partial_apply_caller19closure_with_stringSSTf1cC0_n : $@convention(thin) (@owned String) -> Builtin.Int1
// CHECK: = apply [[F]]([[C]]) : $@convention(thin) (@owned String) -> Builtin.Int1
// CHECK: } // end sil function 'test_multi_closure_with_string'
sil [ossa] @test_multi_closure_with_string : $@convention(thin) (@owned String) -> () {
bb0(%0 : @owned $String):
%2 = function_ref @closure_with_string : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
%26 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
%27 = copy_value %26
%28 = function_ref @simple_multiple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%29 = apply %28(%26, %27) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%9999 = tuple()
return %9999 : $()
}