2024年Clojure状态调查!中分享您的想法。

欢迎!请查看关于页面以获取更多关于如何使用本站的信息。

0
编译器

看起来自我递归调用没有被直接链接优化,但如果我们重定义同一函数两次,编译器就会被欺骗认为调用不是递归的,并且(合乎逻辑地)将其优化为 invokeStatic。

我还没有调查原因,但怀疑(可能不正确)这与 :arglist 元数据在变量未定义和已绑定时的不同值有关。

`
[~]> cat test.clj
(ns test)

(defn a [x]
(a x))
[~]> clj
Clojure 1.8.0-master-SNAPSHOT
user=> (compile 'test)
test
user=> ^D
[~]> cd classes
[~/classes]> javap -c test\$a
编译自 "test.clj"
public final class test$a extends clojure.lang.AFunction {
public static final clojure.lang.Var const__0;

public static {};

Code:
   0: ldc           #11                 // String test
   2: ldc           #13                 // String a
   4: invokestatic  #19                 // Method clojure/lang/RT.var:(Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var;
   7: checkcast     #21                 // class clojure/lang/Var
  10: putstatic     #23                 // Field const__0:Lclojure/lang/Var;
  13: return

public test$a();

Code:
   0: aload_0
   1: invokespecial #26                 // Method clojure/lang/AFunction."<init>":()V
   4: return

public static java.lang.Object invokeStatic(java.lang.Object);

Code:
   0: getstatic     #23                 // Field const__0:Lclojure/lang/Var;
   3: invokevirtual #32                 // Method clojure/lang/Var.getRawRoot:()Ljava/lang/Object;
   6: checkcast     #34                 // class clojure/lang/IFn
   9: aload_0
  10: aconst_null
  11: astore_0
  12: invokeinterface #37,  2           // InterfaceMethod clojure/lang/IFn.invoke:(Ljava/lang/Object;)Ljava/lang/Object;
  17: areturn

public java.lang.Object invoke(java.lang.Object);

Code:
   0: aload_1
   1: aconst_null
   2: astore_1
   3: invokestatic  #41                 // Method invokeStatic:(Ljava/lang/Object;)Ljava/lang/Object;
   6: areturn

}`

重新定义同一函数两次可以实现此功能。

`
[~]> cat test.clj
(ns test)

(defn a [x]
(a x))

(defn a [x]
(a x))
[~]> clj
Clojure 1.8.0-master-SNAPSHOT
user=> (compile 'test)
test
user=> ^D
[~]> cd classes
[~/classes]> javap -c test\$a
编译自 "test.clj"
public final class test$a extends clojure.lang.AFunction {
public static final clojure.lang.Var const__0;

public static {};

Code:
   0: ldc           #11                 // String test
   2: ldc           #13                 // String a
   4: invokestatic  #19                 // Method clojure/lang/RT.var:(Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var;
   7: checkcast     #21                 // class clojure/lang/Var
  10: putstatic     #23                 // Field const__0:Lclojure/lang/Var;
  13: return

public test$a();

Code:
   0: aload_0
   1: invokespecial #26                 // Method clojure/lang/AFunction."<init>":()V
   4: return

public static java.lang.Object invokeStatic(java.lang.Object);

Code:
   0: aload_0
   1: aconst_null
   2: astore_0
   3: invokestatic  #30                 // Method invokeStatic:(Ljava/lang/Object;)Ljava/lang/Object;
   6: areturn

public java.lang.Object invoke(java.lang.Object);

Code:
   0: aload_1
   1: aconst_null
   2: astore_1
   3: invokestatic  #30                 // Method invokeStatic:(Ljava/lang/Object;)Ljava/lang/Object;
   6: areturn

}`

3 答案

0

评论者:bronsa

我快速看了一下这个问题,不太容易解决,因为处理递归调用可能需要遍历整个 defn AST 两次,一次确定 defn 是否可进行直接链接,另一次使用 StaticInvokeExpr 而非 InvokeExpr 重新构建 AST(使用分析信息而不是反射来使用存根方法)。

0

评论者:alexmiller

是的,Rich 了解这个问题,但由于您提到的问题尚未解决。这非常困难!

0
参考:[a href="https://clojure.atlassian.net/browse/CLJ-1865" rel="nofollow" target="_blank">https://clojure.atlassian.net/browse/CLJ-1865
...