Как обеспечить полную компиляцию байт-кода? - PullRequest
0 голосов
/ 26 апреля 2019

Я использую TCL8.6.8.

Вот мой эксперимент:

>cat ~/tmp/1.tcl
proc p {} {
foreach i {a b c} {
    if {$i == "b"} {
        break
    }
    puts $i
}
}

Теперь я вхожу в tclsh:

% proc disa {file_name} {
    set f [open $file_name r]
    set data [read -nonewline $f]
    close $f
    tcl::unsupported::disassemble script $data
}

% disa ~/tmp/1.tcl
ByteCode 0x0x55cabfc393b0, refCt 1, epoch 17, interp 0x0x55cabfbdd990 (epoch 17)
  Source "proc p {} {\nforeach i {a b c} {\n    if {$i == \"b\"} ..."
  Cmds 1, src 175, inst 11, litObjs 4, aux 0, stkDepth 4, code/src 1.26
  Code 220 = header 168+inst 11+litObj 32+exc 0+aux 0+cmdMap 4
  Commands 1:
      1: pc 0-9, src 0-87
  Command 1: "proc p {} {\nforeach i {a b c} {\n    if {$i == \"b\"} ..."
    (0) push1 0     # "proc"
    (2) push1 1     # "p"
    (4) push1 2     # ""
    (6) push1 3     # "\nforeach i {a b c} {\n    if {$i == \"b..."
    (8) invokeStk1 4 
    (10) done 

Вы можете видеть, что этоне полностью скомпилирован в байт-код, так как сценарий вложенности foreach принимается за буквальную строку.

Теперь я использую tcl::unsupported::disassemble proc вместо tcl::unsupported::disassemble script, я могу получить полностью скомпилированную версию байт-кода:

% source ~/tmp/1.tcl

% tcl::unsupported::disassemble proc p
ByteCode 0x0x55cabfc393b0, refCt 1, epoch 17, interp 0x0x55cabfbdd990 (epoch 17)
  Source "\nforeach i {a b c} {\n    if {$i == \"b\"} {\n        ..."
  File "/home/jibin/tmp/1.tcl" Line 1
  Cmds 4, src 76, inst 54, litObjs 4, aux 1, stkDepth 5, code/src 4.21
  Code 320 = header 168+inst 54+litObj 32+exc 28+aux 16+cmdMap 16
  Proc 0x0x55cabfc72820, refCt 1, args 0, compiled locals 1
      slot 0, scalar, "i"
  Exception ranges 1, depth 1:
      0: level 0, loop, pc 7-47, continue 49, break 50
  Commands 4:
      1: pc 0-52, src 1-74        2: pc 7-41, src 25-60
      3: pc 23-36, src 50-54        4: pc 42-47, src 66-72
  Command 1: "foreach i {a b c} {\n    if {$i == \"b\"} {\n        br..."
    (0) push1 0     # "a b c"
    (2) foreach_start 0 
        [jumpOffset=-42, vars=[%v0]]
  Command 2: "if {$i == \"b\"} {\n        break\n ..."
    (7) startCommand +34 1  # next cmd at pc 41, 1 cmds start here
    (16) loadScalar1 %v0    # var "i"
    (18) push1 1    # "b"
    (20) eq 
    (21) jumpFalse1 +18     # pc 39
  Command 3: "break..."
    (23) startCommand +14 1     # next cmd at pc 37, 1 cmds start here
    (32) jump4 +18  # pc 50
    (37) jump1 +4   # pc 41
    (39) push1 2    # ""
    (41) pop 
  Command 4: "puts $i..."
    (42) push1 3    # "puts"
    (44) loadScalar1 %v0    # var "i"
    (46) invokeStk1 2 
    (48) pop 
    (49) foreach_step 
    (50) foreach_end 
    (51) push1 2    # ""
    (53) done 

Вот мой вопрос: почему tcl::unsupported::disassemble script не компилирует скрипт полностью?Команда foreach находится внутри процедуры, я думаю, что функция компиляции proc вызывает функцию компиляции каждой команды, поэтому функция компиляции команды foreach вызывается независимо.

1 Ответ

1 голос
/ 26 апреля 2019

Tcl откладывает компиляцию скрипта или процедуры до тех пор, пока в первый раз не понадобится байт-кодированная версия скрипта / процедуры. Компиляция довольно быстрая (и тщательно кешируется, где это имеет смысл), а оптимизатор в 8.6 облегчен (просто убивает некоторые последовательности кода глупости, которые раньше генерировались), так что это обычно не большая проблема. Степень компиляции для конкретной команды сильно варьируется: expr почти всегда глубоко компилируется (если возможно!), А proc сам никогда не компилируется; то, что вы видите в разборке, - это обобщенная компиляция вызовов команд (поместите слова в стек, вызовите общую команду с таким количеством слов, работа сделана). Это имеет смысл, потому что большинство вызовов proc происходят только один раз, и только на самом деле настраивают вещи на интересные события, которые произойдут позже. Вероятность того, что мы изменим proc сам для получения глубокой компиляции (в отличие от процедур, которые он создает), равна нулю, по крайней мере, для 8,7 / 9,0 и, вероятно, намного впереди. Просто невозможно победить, чтобы оправдать работу, которую он предпримет.

Однако, если вы хотите запустить компиляцию процедуры раньше, вы можете . Все, что нужно, это немного вызвать ...

trace add execution proc leave {apply {{cmdArgs code result op} {
    if {$code == 0} {
        # proc succeeded; it must have been called as: proc name args body
        set procedureName [lindex $cmdArgs 1]
        # Make sure we resolve the procedure name in the right namespace!
        set compTime [lindex [time {
             uplevel 1 [list tcl::unsupported::getbytecode proc $procedureName]
        }] 0]
        # We're done now! Totally optional print of how long it took…
        puts stderr "Compiled $procedure in $compTime µs"
    }
}}}

Я думаю , что getbytecode немного быстрее, чем disassemble (он делает то же самое, но выдает машиночитаемый вывод), но я могу ошибаться. Вам нужно будет использовать disassemble, если код будет использоваться в 8.5.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...