pschachte / wybe

A programming language supporting most of both declarative and imperative programming
MIT License
43 stars 6 forks source link

Procedures with inline flag may not be inlined. #103

Closed CaviarChen closed 3 years ago

CaviarChen commented 3 years ago

I was trying to fix #87 but encountered this.

For example, the following code will have two generated procedure for range.

pub type int_list { pub [] | [|](head:int, tail:int_list) }

pub def range(start:int, stop:int, step:int, ?result:int_list) {
    ?result = []
    do {
        while start < stop
        ?result = [start | result]
        ?start = start + step
    }
    reverse(!result)
}

pub def reverse(lst:int_list):int_list = reverse_helper(lst, [])

def reverse_helper(lst:int_list, acc:int_list):int_list =
    if lst = [?h | ?t] then reverse_helper(t, [h|acc]) else acc
gen$1 > (2 calls)
0: gen$1(result#0:int_list.int_list, start#0:wybe.int, step#0:wybe.int, stop#0:wybe.int, tmp$0#0:int_list.int_list, ?result#1:int_list.int_list):
 AliasPairs: []
 InterestingCallProperties: [InterestingUnaliased 0]
 MultiSpeczDepInfo: [(3,(int_list.reverse_helper<0>,fromList [NonAliasedParamCond 0 [0]]))]
    foreign llvm icmp_slt(start#0:wybe.int, stop#0:wybe.int, ?tmp$3#0:wybe.bool) @wybe:32:35
    case ~tmp$3#0:wybe.bool of
    0:
        int_list.reverse_helper<0>(~%result#0:int_list.int_list, 0:int_list.int_list, ?%result#1:int_list.int_list) #3 @int_list:14:42

    1:
        int_list.gen$2<0>(~result#0:int_list.int_list, ~start#0:wybe.int, ~step#0:wybe.int, ~stop#0:wybe.int, ~tmp$0#0:int_list.int_list, ?result#1:int_list.int_list) #1

gen$2 > {inline} (1 calls)
0: gen$2(result#0:int_list.int_list, start#0:wybe.int, step#0:wybe.int, stop#0:wybe.int, tmp$0#0:int_list.int_list, ?result#2:int_list.int_list):
 AliasPairs: []
 InterestingCallProperties: []
    foreign lpvm alloc(16:wybe.int, ?tmp$5#0:int_list.int_list)
    foreign lpvm mutate(~tmp$5#0:int_list.int_list, ?tmp$6#0:int_list.int_list, 0:wybe.int, 1:wybe.int, 16:wybe.int, 0:wybe.int, start#0:wybe.int)
    foreign lpvm mutate(~tmp$6#0:int_list.int_list, ?tmp$7#0:int_list.int_list, 8:wybe.int, 1:wybe.int, 16:wybe.int, 0:wybe.int, ~result#0:int_list.int_list)
    foreign llvm add(~start#0:wybe.int, step#0:wybe.int, ?tmp$2#0:wybe.int) @wybe:20:34
    int_list.gen$1<0>(~tmp$7#0:int_list.int_list, ~tmp$2#0:wybe.int, ~step#0:wybe.int, ~stop#0:wybe.int, ~tmp$0#0:int_list.int_list, ?result#2:int_list.int_list) #2 @int_list:5:5

range > public {inline} (0 calls)
0: range(start#0:wybe.int, stop#0:wybe.int, step#0:wybe.int, ?result#1:int_list.int_list):
 AliasPairs: []
 InterestingCallProperties: []
    int_list.gen$1<0>(0:int_list.int_list, ~start#0:wybe.int, ~step#0:wybe.int, ~stop#0:wybe.int, 0:int_list.int_list, ?result#1:int_list.int_list) #1 @int_list:5:5

Note that gen$2 has the inline flag but it is not inlined in gen$1. This is a problem because we skip procedures with the inline flag during alias analysis. In this case, we will miss the optimization opportunities in gen$2. Even worse, the compiler may be misleaded by the empty AliasPairs of a procedure with the inline flag.

CaviarChen commented 3 years ago

https://github.com/pschachte/wybe/commit/76cb705388277edcd7aab733692309eba568833c introduces an new bug. Compiling the code above gives:

Internal error: arguments in call list.reverse_helper<0>(~tmp$14#0:list.int_list, ~tmp$15#0:wybe.int, ~step#0:wybe.int, ~stop#0:wybe.int, ~tmp$0#0:list.int_list, ?result#1:list.int_list) #3 don't match params [lst#0:list.int_list,acc#0:list.int_list,?$#0:list.int_list]
CallStack (from HasCallStack):
  error, called at src/AST.hs:3161:17 in main:AST
pschachte commented 3 years ago

I'm trying to track this down, but I don't understand why it happens. It looks like some code is incorrectly transformed. Here's the last part of the log of Transform and BodyBuilder:

Transform: --- prim:           reverse.gen$1<0>(~tmp$14#0:reverse.int_list, ~tmp$15#0:wybe.int, ~step#0:wybe.int, ~stop#0:wybe.int, ~tmp$0#0:reverse.int_list, ?result#1:reverse.int_list) #3 @reverse:5:5
Transform: --- transformed to: reverse.reverse_helper<0>(~tmp$14#0:reverse.int_list, ~tmp$15#0:wybe.int, ~step#0:wybe.int, ~stop#0:wybe.int, ~tmp$0#0:reverse.int_list, ?result#1:reverse.int_list) #3 @reverse:5:5
BodyBuilder: Expanded tmp$14#0 to Nothing
BodyBuilder: Expanded tmp$15#0 to Nothing
BodyBuilder: Expanded step#0 to Nothing
BodyBuilder: Expanded stop#0 to Nothing
BodyBuilder: Expanded tmp$0#0 to Nothing
Internal error: arguments in call reverse.reverse_helper<0>(~tmp$14#0:reverse.int_list, ~tmp$15#0:wybe.int, ~step#0:wybe.int, ~stop#0:wybe.int, ~tmp$0#0:reverse.int_list, ?result#1:reverse.int_list) #3 don't match params [lst#0:reverse.int_list,acc#0:reverse.int_list,?$#0:reverse.int_list]

So it looks to me like a call to reverse.gen$1<0> is being transformed into a call to reverse.reverse_helper<0> with the same arguments, which is where it goes off the rails.