Clozure CL中文版019:修改Clozure CL

  • 0

Clozure CL中文版019:修改Clozure CL

Category:帮助手册 Tags : 

修改Clozure CL

将代码贡献回Clozure CL项目

此部分是占位符,自2004年8月起添加。正在编写全文,并将在可用时立即添加。

在“开发”和“用户”模式下使用Clozure CL

在分发时,Clozure CL启动时将* PACKAGE *设置为CL-USER包,并且大多数预定义的功能和方法都可以防止意外重新定义。包设置当然是ANSI CL的要求,并且预定义函数和方法的保护旨在捕获某些类型的编程错误(意外地重新定义CL或CCL函数),然后这些错误有可能造成很大的损害。

这些设置可能会使用Clozure CL来开发Clozure CL有点尴尬,因为大部分过程假设您在CCL包中工作是最新的,Clozure CL开发的主要目的是重新定义一些预定义的内置函数。从源代码构建Clozure CL的标准“例程”方法(请参阅参考资料) – COMPILE-CCL,XCOMPILE-CCL和XLOAD-LEVEL-0 – 将* PACKAGE *绑定到“CCL”包并启用预定义函数的重新定义; 此外,符号COMPILE-CCL,XCOMPILE-CCL和XLOAD-LEVEL-0现在也从“CCL”包中导出。

在Clozure CL上编译和/或加载单个文件的一些其他(更临时)的开发方式,逐步重新定义单个函数 – 可能会很麻烦,除非一个人恢复到传统上在Clozure CL中提供的操作模式。一些Clozure CL源文件 – 尤其是那些构成自举图像源和“冷加载”序列中的前几个文件的文件 – 被编译并加载到“CCL”包中但不包含(IN-PACKAGE“CCL”)形式,因为IN-PACKAGE直到冷负荷序列的后期才能工作。

SET-USER-ENVIRONMENT和SET-DEVELOPMENT-ENVIRONMENT关于它们影响的特殊变量的一些奇怪的行为旨在允许这些构造在read-eval-print循环下一次返回到顶层时生效。 ?’提示; 例如,构造可以在LOAD中有意义地使用(回想一下LOAD绑定* PACKAGE *),尽管在同一个LOAD调用中使用这两个构造可能会非常混乱。

“用户”和“发展”是非常通用的术语; 在这里,他们的目的是强制“使用”Clozure CL和“开发”它之间的区别。

保存Clozure CL图像的初始环境是刚刚调用(SET-USER-ENVIRONMENT T)的环境; 在以前的版本中,它实际上就好像刚刚调用了(SET-DEVELOPMENT-ENVIRONMENT T)。

希望Clozure CL的大多数用户可以在大多数时间安全地忽略这些问题。请注意,在将自己的代码(或第三方代码)加载到Clozure CL之后执行(SET-USER-ENVIRONMENT T)将保护该代码(以及Clozure CL)免于意外重新定义; 在某些情况下可能有用。

内核调试器

在一个完美的世界中,这样的事情不可能发生:

Welcome to Clozure CL Version x.y!

? (defun foo (x)

(declare (cons x))

(cdr x))

FOO

 

? (foo -1) ;Oops. Too late …

Unhandled exception 11 at 0x300e90c8, context->regs at #x7ffff6b8

Continue/Debugger/eXit <enter>?

你可能已经注意到,这不是一个完美的世界; 很少有原因(尝试引用-1的CDR,因此访问位置0附近的未映射的内存)这种效果(“未处理的异常…”消息)是如此明显。

除非您使用GDB调试内核,否则上面消息中打印的地址不是很有用(如果您使用它们,它们通常非常有用)。

除了导致lisp内核不知道如何处理的异常之外,还可以故意进入内核调试器(更多):

? (defun classify (n)

(cond ((> n 0) “Greater”)

((< n 0) “Less”)

(t

;; Sheesh ! What else could it be ?

(ccl::bug “I give up. How could this happen ?”))))

CLASSIFY

 

? (classify 0)

Bug in Clozure CL system code:

I give up. How could this happen ?

? for help

[12345] Clozure CL kernel debugger:

CCL :: BUG不是这个例子的正确工具(调用BREAK或PRINT可能会更好地清除这个谜团),但是当有些其他工具无法使用时,它有时会很有用。例如,lisp错误系统会注意到,如果尝试发出错误信号本身会导致错误发出信号; 如果CLOS或I / O系统损坏或丢失,就会发生这种情况。在少量递归错误之后,错误系统放弃并调用CCL :: BUG。

如果一个人输入’?’ 在内核调试器提示符下,会看到如下输出:

(S)  Find and describe symbol matching specified name(B)  Show backtrace(X)  Exit from this debugger, asserting that any exception was handled(K)  Kill Clozure CL process(?)  Show this help

CCL :: BUG只是对lisp内核进行FF-CALL。如果由于未处理的异常(例如非法内存引用)而调用了内核调试器,则OS内核会将数据结构中的机器状态(“上下文”)保存到我们,在这种情况下,可以使用一些其他选项来显示异常点寄存器的内容。另一个函数-CCL :: DBG-导致生成一个特殊异常,并使用非null“context”进入lisp内核调试器:

? (defun classify2 (n)

(cond ((> n 0) “Greater”)

((< n 0) “Less”)

(t (dbg n))))

CLASSIFY2

 

? (classify2 0)

Lisp Breakpoint

While executing: #<Function CLASSIFY2 #x08476cfe>

? for help

[12345] Clozure CL kernel debugger: ?

(G)  Set specified GPR to new value

(A)  Advance the program counter by one instruction (use with caution!)

(D)  Describe the current exception in greater detail

(R)  Show raw GPR/SPR register values

(L)  Show Lisp values of tagged registers

(F)  Show FPU registers

(S)  Find and describe symbol matching specified name

(B)  Show backtrace

(X)  Exit from this debugger, asserting that any exception was handled

(P)  Propagate the exception to another handler (debugger or OS)

(K)  Kill Clozure CL process

(?)  Show this help

CCL :: DBG接受一个参数,其值被复制到Clozure CL用于返回函数主值的寄存器中(arg_z,即PowerPC上的r23)。如果我们此时选择(L)选项,我们会看到如下的延迟:

rnil = 0x01836015nargs = 0r16 (fn) = #<Function CLASSIFY2 #x30379386>r23 (arg_z) = 0r22 (arg_y) = 0r21 (arg_x) = 0r20 (temp0) = #<26-element vector subtag = 2F @#x303793ee>r19 (temp1/next_method_context) = 6393788r18 (temp2/nfn) = #<Function CLASSIFY2 #x30379386>r17 (temp3/fname) = CLASSIFY2r31 (save0) = 0r30 (save1) = *TERMINAL-IO*r29 (save2) = 0r28 (save3) = (#<RESTART @#x01867f2e> #<RESTART @#x01867f56>)r27 (save4) = ()r26 (save5) = ()r25 (save6) = ()r24 (save7) = ()

由此我们可以得出结论,CLASSIFY2的有问题的参数是0(参见r23 / arg_z),我需要研究一个更好的例子。

R选项以十六进制显示ALU(和PPC分支单元)寄存器的值; F选项显示FPU寄存器的值。

(B)选项显示原始堆栈回溯; 它会尝试识别外部函数以及lisp函数。(外部函数名称是基于最近的前一个导出符号的猜测。)

如果您意外地发现自己处于“lisp内核调试器”中,(L)和(B)选项的输出通常是错误报告中最有用的内容。

在Clozure CL LAP函数中使用AltiVec

概观

现在可以在PPC LAP(汇编程序)函数中使用AltiVec指令。

lisp内核检测AltiVec的存在与否,并在lisp线程切换上保留AltiVec状态并响应异常,但实现不会使用向量操作。

本文档一般不记录PPC LAP编程。理想情况下,会有一些文件。

本文档解释了Clozure CL中的AltiVec寄存器使用约定,并解释了一些有助于实施这些约定的lap宏的使用。

下面描述的所有全局符号都是从CCL包导出的。请注意,lap宏名称,ppc指令名称和(在大多数情况下)寄存器名称被视为字符串,因此这仅适用于函数和全局变量名称。

Clozure CL对AltiVec LAP编程的大部分支持是基于Shannon Spiers为MCL做出的贡献。

注册使用惯例

使用AltiVec指令的Clozure CL LAP函数必须相互之间以及与C函数互操作; 这一事实表明他们遵循C AltiVec注册使用惯例。(vr0-vr1 scratch,vr2-vr13参数/返回值,vr14-vr19 temporaries,vr20-vr31 callee-save非易失性寄存器。)

LinuxPPC中使用的EABI(嵌入式应用程序二进制接口)并没有特别重视vrsave专用寄存器; 在其他平台(特别是MacOS)上,它用作位图,向系统级代码指示哪些向量寄存器包含有意义的值。

WITH-ALTIVEC-REGISTERS lap宏生成代码,用于在需要此功能的平台上保存,更新和恢复VRSAVE(由控制此行为的特殊变量的值指示),并在不需要它的平台上忽略VRSAVE要维持。

在所有PPC平台上,必须在分配给它们之前保存任何非易失性向量寄存器(vr20 .. vr31),并在返回给调用者之前恢复这些寄存器。

在需要维护VRSAVE的平台上,没有必要提及用作传入参数的向量寄存器的“使用”。在WITH-ALTIVEC-REGISTERS表单中提及它们的使用并不是错误的,但在许多有趣的情况下它可能是不必要的。同样可以假设在vr2中返回向量值的任何函数的调用者已经在VRSAVE中设置了适当的位以指示该寄存器是活动的。因此,可以编写一个叶函数,该函数在vr3和vr2中添加了字节,并将结果返回到vr2中:

(defppclapfunction vaddubs ((y vr3) (z vr2))  (vaddubs z y z)  (blr))

当在LAP函数中使用非传入参数的向量寄存器时,WITH-ALTIVEC-REGISTERS负责维护VRSAVE并保存/恢复任何非易失性向量寄存器:

(defppclapfunction load-array ((n arg_z))  (check-nargs 1)  (with-altivec-registers (vr1 vr2 vr3 vr27) ; Clobbers imm0    (li imm0 arch::misc-data-offset)    (lvx vr1 arg_z imm0)                ; load MSQ    (lvsl vr27 arg_z imm0)              ; set the permute vector    (addi imm0 imm0 16)                 ; address of LSQ    (lvx vr2 arg_z imm0)                ; load LSQ    (vperm vr3 vr1 vr2 vr27)           ; aligned result appears in VR3    (dbg t))                         ; Look at result in some debugger  (blr))

CATCH和UNWIND-PROTECT不保留AltiVec寄存器。由于AltiVec只能从Clozure CL中的LAP访问,并且由于LAP功能很少使用高级控制结构,因此在实践中这应该很少成为问题。

使用非易失性向量寄存器的LAP函数和可能使用CATCH或UNWIND-PROTECT的调用(Lisp?)代码应该在这样的调用之前保存这些向量寄存器并在返回时恢复它们。这是WITH-VECTOR-BUFFER lap宏的预期用途之一。

发展模式词典

*warn-if-redefine-kernel*[变量]

如果为true,则尝试重新定义(通过DEFUN或DEFMETHOD)标记为“预定义”的函数和方法会发出可持续的错误信号。

请注意,这些是CERROR,而不是警告,并且自1987年左右以来,在MCL或Clozure CL的内核中没有定义lisp函数或方法。

set-development-environment &optional unmark-builtin-functions[功能]

安排* PACKAGE *和* WARN-IF-REDEFINE-KERNEL *的最外层特殊绑定分别将“CCL”包和NIL的值恢复到这些变量。如果可选参数为true,则将所有全局定义的函数和方法标记为“未预定义”(这是一项相当昂贵的操作。)

set-user-environment &optional mark-builtin-functions[功能]

安排* PACKAGE *和* WARN-IF-REDEFINE-KERNEL *的最外层特殊绑定分别将“CL-USER”包和T的值恢复为这些变量。如果可选参数为true,则将所有全局定义的函数和方法标记为“预定义”(这是一个相当昂贵的操作。)

*altivec-available*[变量]

每次基于lisp内核提供的信息启动Clozure CL会话时,都会初始化此变量。如果AltiVec存在则其值为true,否则为false。不应由用户代码设置此变量。

altivec-available-p[功能]

如果AltiVec可用,则返回非NIL。

*altivec-lapmacros-maintain-vrsave-p*[变量]

旨在控制某些搭接宏的扩展。在LinuxPPC上初始化为NIL; 在平台(例如MacOS X / Darwin)上初始化为T,要求VRSAVE SPR始终包含有效向量寄存器的位掩码。

with-altivec-registers reglist &body body[Lap Macro]

指定正文中使用的Altivec寄存器的测试。

寄存器列表

向量寄存器名称列表(vr0 .. vr31)。

身体

一系列PPC LAP指令。

指定正文中使用的AltiVec寄存器集。如果扩展宏时* altivec-lapmacros-maintain-vrsave-p *为真,则生成代码以保存VRSAVE SPR并更新VRSAVE以包含从指定寄存器列表生成的位掩码。生成代码,保存出现在寄存器列表中的任何非易失性向量寄存器,执行主体,并恢复保存的非易失性向量寄存器(如果* altivec-lapmacros-maintain-vrsave-p *为真,则将VRSAVE恢复为好吧。使用IMM0寄存器(r3)作为临时寄存器。

with-vector-buffer base n &body body[Lap Macro]

为已保存的Altivec寄存器腾出空间。

基础

任何可用的通用寄存器。

ñ

1到254之间的整数,包括1和254。(通常应该更接近1)。以16字节为单位指定缓冲区的大小。

身体

一系列PPC LAP指令。

生成代码,该代码分配一个16字节对齐的缓冲区,其大小足以包含N个向量寄存器; GPR基地指向此缓冲区的最低地址。处理完主体后,缓冲区将被释放。只要需要引用缓冲区,正文就应该保留base的值。它旨在将base用作正文中stvx和lvx指令的基址寄存器。

http://mip.i3geek.com

Leave a Reply

搜索

分类目录

公 告

本网站学习论坛:

www.zhlisp.com

lisp中文学习源码:

https://github.com/zhlisp/

欢迎大家来到本站,请积极评论发言;

加QQ群学习交流。