LINUX.ORG.RU

Ответ на: комментарий от nanoolinux

Я имею ввиду примитивный. Там не должно быть сложно.

deterok ★★★★★
() автор топика
Ответ на: комментарий от nanoolinux

Не уверен, что может быть просто.

это если «замкнуть контекст на лямбду» и никак по-другому, то да, сложно, особенно если в llvm-ir такого нет :) А если «закат солнца в ручную», то что мешает выделять память для данных/указателей, копировать данные, передавать их функции через доп. параметр или через паблик указатель?

yyk ★★★★★
()
Ответ на: комментарий от deterok

По-моему в IR нет замыканий. То есть, если тебе нужно нужно замыкание в IR, то ты хочешь чего-то не того. Если тебе нужно замыкание в реализуемом «языке», сделай его сам из примитивов, предлагаемых IR.

staseg ★★★★★
()
Ответ на: комментарий от staseg

Вот, мне нужно в языке, но как это правильно организовать это я не знаю. Нужна теория и наставник.

deterok ★★★★★
() автор топика

http://llvm.org/docs/LangRef.html#trampoline-intrinsics

declare void @llvm.init.trampoline(i8*, i8*, i8*);

declare i8* @llvm.adjust.trampoline(i8*);

define i32 @plus(i32* nest %x, i32 %y) {
    %x_ = load i32* %x
    %r = add i32 %x_, %y
    ret i32 %r
}

define i32 @main(i32, i8**) {
    %c = alloca i32
    store i32 2, i32* %c
    %cp = bitcast i32* %c to i8*
    %t = alloca [32 x i8], align 4
    %tp = getelementptr [32 x i8]* %t, i32 0, i32 0
    call void @llvm.init.trampoline(i8* %tp, i8* bitcast (i32(i32*, i32)* @plus to i8*), i8* %cp)
    %tf = call i8* @llvm.adjust.trampoline(i8* %tp)
    %plus_two = bitcast i8* %tf to i32(i32)*
    %r = call i32 %plus_two(i32 5)
    ret i32 %r
}
➜  ~  llc closure.ll       
➜  ~  gcc closure.s        
➜  ~  ./a.out              
➜  ~  echo $?              
7

closures или nested functions как в GCC так делаются.

Получается

	.file	"closure.ll"
	.text
	.globl	plus
	.align	16, 0x90
	.type	plus,@function
plus:                                   # @plus
	.cfi_startproc
# BB#0:
	addl	(%r10), %edi
	movl	%edi, %eax
	ret
.Ltmp0:
	.size	plus, .Ltmp0-plus
	.cfi_endproc

	.globl	main
	.align	16, 0x90
	.type	main,@function
main:                                   # @main
	.cfi_startproc
# BB#0:
	subq	$40, %rsp
.Ltmp2:
	.cfi_def_cfa_offset 48
	movl	$2, 36(%rsp)
	leaq	36(%rsp), %rax
	movq	%rax, 16(%rsp)
	movb	$-29, 26(%rsp)
	movw	$-183, 24(%rsp)
	movw	$-17847, 14(%rsp)       # imm = 0xFFFFFFFFFFFFBA49
	movq	$plus, 6(%rsp)
	movw	$-17591, 4(%rsp)        # imm = 0xFFFFFFFFFFFFBB49
	leaq	4(%rsp), %rax
	movl	$5, %edi
	callq	*%rax
	addq	$40, %rsp
	ret
.Ltmp3:
	.size	main, .Ltmp3-main
	.cfi_endproc
quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Но выглядит тормозным (это оно машинный код на стеке пишет во время выполнения) и неудобным (максимум один nest).

Поэтому на уровне фронтенда и рантаймовых фишек (чтобы не говорить «VM»). Например:

f(x, y) = x + y
g(x) = \y -> f x y

будет

struct f_int_int_int_capture_first { int x; int(&f)(int, int); };
int call(f_int_int_int_capture_first &fn, int y) { return fn.f(fn.x, y); }
int f(int x, int y) { return x + y; }
f_int_int_int_capture_first& g(int x) { return *new /* gc_new */ f_int_int_int_capture_first{x, f}; }
int main() { return call(g(1), 2); }

В фронтенде всё знаем — про типы и захваты, так что аллоцируем такие структуры и сами расставляем call, в IR для этого всё есть. Типичная проблема с необходимостью счётчика ссылок или GC для замыканий — тоже имеет место быть. Ну и разные агрессивные оптимизации к ним применимы, как в C++ или в Haskell.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

Спасибо, очень интересно, буду разбираться.

deterok ★★★★★
() автор топика

google://closure conversion, lambda lifting

rand
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.