在llvm绑定中将函数作为形式参数传递时,上下文减少堆栈溢出

时间:2011-06-25 04:14:29

标签: haskell llvm

我正在尝试解决使用Haskell llvm绑定时发生的编译时错误。

代码

-- Line 14 follows
type Acc = Int32 -> Int32 -> IO Int32
type Sig = Int32 -> Ptr Int32 -> Function Acc-> IO Int32

-- [...]

-- Line 31 follows
mSum :: CodeGenModule (Function Sig)
mSum = createNamedFunction ExternalLinkage "sum" $ \l ptr_x fn ->  do
    r <- forLoop (valueOf 0) l (valueOf (0::Int32)) $ \i sum -> do
      xi <- getIndex ptr_x i
      x <- load xi
      call fn sum x
    ret r 

Comentary mSum是一个monadic函数,它为llvm函数生成一个咬合代码。生成的函数用于获取三个参数:l:整数数组的长度; ptr_x指向整数数组的指针;和fn一个函数。 (第32行)生成的函数将使用名为sum的累加器循环遍历数组的元素。对于每个值,xsumx将传递给函数fn。该函数的结果将在下一次循环时变为sum的值。 sum的最终值将作为生成函数的值返回。

错误

llvm3.hs:32:8:
Context reduction stack overflow; size = 21
Use -fcontext-stack=N to increase stack size to N
  $dFunctionArgs :: FunctionArgs
                      (Function Acc -> b17)
                      (Value (Ptr (Int32 -> Int32 -> IO Int32)) -> b'17)
                      r18
  $dFunctionArgs :: FunctionArgs
                      (Ptr (Int32 -> Int32 -> IO Int32) -> b16)
                      (Value (Function Acc) -> b'16)
                      r17

  -- [ ... ]

  $dFunctionArgs :: FunctionArgs
                      (Ptr (Int32 -> Int32 -> IO Int32) -> b4)
                      (Value (Function Acc) -> b'4)
                      r5
  $dFunctionArgs :: FunctionArgs
                      (Function Acc -> b3)
                      (Value (Ptr (Int32 -> Int32 -> IO Int32)) -> b'3)
                      r4
  $dFunctionArgs :: FunctionArgs
                      (Ptr (Int32 -> Int32 -> IO Int32) -> b2)
                      (Value (Function Acc) -> b'2)
                      r3
  $dFunctionArgs :: FunctionArgs
                      (Function Acc -> IO Int32)
                      (Function (Int32 -> Int32 -> IO Int32)
                       -> CodeGenFunction r Terminate)
                      (CodeGenFunction r0 ())
  $dFunctionArgs :: FunctionArgs
                      (Ptr a0 -> b1) (Value (Ptr Int32) -> b'1) r2
  $dFunctionArgs :: FunctionArgs
                      (Ptr Int32 -> Function Acc -> IO Int32)
                      (Value (Ptr a0)
                       -> Function (Int32 -> a0 -> IO Int32)
                       -> CodeGenFunction r Terminate)
                      (CodeGenFunction r0 ())
  $dFunctionArgs :: FunctionArgs (i0 -> b) (Value Int32 -> b') r1
  $dFunctionArgs :: FunctionArgs
                      Sig
                      (Value i0
                       -> Value (Ptr a0)
                       -> Function f0
                       -> CodeGenFunction r19 Terminate)
                      (CodeGenFunction r0 ())
In the expression: createNamedFunction ExternalLinkage "sum"
In the expression:
    createNamedFunction ExternalLinkage "sum"
  $ \ l ptr_x fn
      -> do { r <- forLoop (valueOf 0) l (valueOf (0 :: Int32))
                 $ \ i sum -> ...;
              ret r }
In an equation for `mSum':
    mSum
      = createNamedFunction ExternalLinkage "sum"
      $ \ l ptr_x fn
          -> do { r <- forLoop (valueOf 0) l (valueOf (0 :: Int32)) $ ...;
                  .... }

问题:有两个可能的问题:如果我没有正确传递函数,那么如何将指针传递给LLVM中的函数?否则我需要做什么来满足类型检查器?

除了:我不太了解Haskell的工作,足以理解我为什么会遇到这个错误。我也不理解createNamedFunction上的类型签名:

(IsFunction f, FunctionArgs f g (CodeGenFunction r ()))  
=> Linkage   
-> String    
-> g    -- Function body.
-> CodeGenModule (Function f)  

1 个答案:

答案 0 :(得分:9)

哦,好悲伤

是的,您可能遇到某种类型错误。这里的库使用UndecidableInstances来做一些花哨的类型级逻辑,其中“花哨”意味着“如果你运气不好就会导致类型检查器进入无限循环”。你可能猜到你现在有多幸运。不幸的是,虽然由此产生的令人震惊的错误告诉我们哪里正在发生循环,但它并不像人们想要的那样提供信息为什么

另外,请注意,不理解为什么会出现此错误是完全合理的。这是混乱和混乱。接下来是一些粗略的解释和我对缩小原因的想法:

首先,在计算FunctionArgs时,循环显然正在发生。考虑类的定义:

class FunctionArgs f g r | f -> g r, g r -> f where
    ...

|之后的部分是功能依赖,指定某些参数唯一地确定其他参数。在这种情况下,f以及gr的组合相互决定,因此它是一种可以在任一方向上计算的双向函数。

您的功能为mSum :: CodeGenModule (Function Sig),使用createNamedFunction的签名统一,Sig作为f参数,rg目前未知。类型同义词Sig会扩展为Int32 -> Ptr Int32 -> Function Acc-> IO Int32。现在,我们可以查看FunctionArgs的{​​{3}},看看这给了我们什么。

运算符优先级为最左边的函数箭头提供Sig的最外层类型构造函数,因此我们找到匹配的实例:FunctionArgs b b' r => FunctionArgs (a -> b) (Value a -> b') r。我们可以替换类型,根据需要进行统一,并重复:

  • FunctionArgs (Ptr Int32 -> Function Acc-> IO Int32) b' r => FunctionArgs (Int32 -> (Ptr Int32 -> Function Acc-> IO Int32)) (Value Int32 -> b') r

  • FunctionArgs (Function Acc-> IO Int32) b' r => FunctionArgs (Ptr Int32 -> (Function Acc-> IO Int32)) (Value (Ptr Int32) -> b') r

您应该能够在出现的错误中将这些步骤与堆栈跟踪相匹配。一个有趣的事情是,堆栈跟踪中有额外的步骤,我不确定其原因 - 与我基于功能依赖性填充类型的方式有关,我想。看起来它首先基于(->)类型构造函数选择实例,然后填充Value a -> b参数的g类型构造函数,然后执行递归步骤(在上下文中)返回统一其余类型。

现在,我们知道在这一点上出现问题的时候;这可以从堆栈溢出的通用原则推断出来,这就是问题就在堆栈跟踪开始反复重复相同模式之前的某个地方。

对于下一次缩减,f使用Function Acc-> IO Int32进行实例化,而gr到目前为止仍未确定(尽管我们知道它们必须由{{唯一确定) 1}})。此时查看f的定义也是个好主意:Function

  • type Function a = Value (Ptr a)

同样,我们使用功能箭头选择实例:FunctionArgs (Value (Ptr Acc) -> IO Int32) g r

...这就是可疑的事情,因为如果你比较上面的堆栈跟踪,它会显示FunctionArgs b b' r => FunctionArgs (a -> b) (Value a -> b') r参数的(Function (...) -> ...)。鉴于上面g的定义,技术上匹配,因为我们期望Function,它是Value类型同义词的最外层构造函数。不幸的是,我们对Function参数也有(Function (...) -> ...),这是不一致的,因为f参数应该有一个额外的g构造函数。

填写错误的结构后,它会继续卡在应该填写其余类型变量的步骤;看起来好像双向功能依赖性导致它来回反复,反复尝试将Valuea统一起来。因此,随着类型同义词的扩展,我们得到了这个:

  • Value a
  • FunctionArgs (Value (Ptr Acc) -> b) (Value (Ptr Acc) -> b') r

... ad infinitum。

此时我真的不确定会导致这场冲突的原因。我期望的结果是FunctionArgs (Ptr Acc -> b) (Value (Value (Ptr Acc)) -> b') r的一致实例选择,我无法真正说出为什么它会被卡住。

编辑:等等,我很傻。我最初误读了你的代码 - 很明显从lambda表达式的推断类型得到了错误类型的一部分,我认为它比实际上更具多态性。特别是,参数FunctionArgs (Value (Ptr Acc) -> b) (Value (Value (Ptr Acc)) -> b') r作为函数fn的参数给出,这就是上面创建的不一致类型。我仍然不知道它为什么会进入无限循环,但至少可以解释冲突。

假设由于call :: CallArgs f g => Function f -> g导致的推断类型正确,call参数的类型应为g,这意味着Value Int32 -> Value (Ptr Int32) -> Function Acc-> CodeGenFunction Int32 ()应为f },而不是您的类型Int32 -> Ptr Int32 -> Ptr Acc-> IO Int32

支持这一点,如果你看一下同样适用于Sig的{​​{1}}类,它希望函数参数是原始类型,如IsFunction或{{1} } s到原始类型,但不是f,这是Int32扩展到的。

毕竟,我认为您的问题只是您的类型Ptr稍有不妥。

...韦尔普。那好吧。