mini iLisp Embedded Procedures - graeme-lockley/ilisp GitHub Wiki

The Problem

The following procedures

(const (range low high)
  (if (< low high) 
        (pair low (range (+ 1 low) high))
      ()
  )
)

(const (sum integers)
  (if (null? integers) 
        0
      (+ (car integers) (sum (cdr integers)))
  )
)

are easily compiled into

define %struct.Value* @range(%struct.Value* %0, %struct.Value* %1) {
  %3 = alloca %struct.Value*, align 8
  %4 = alloca %struct.Value*, align 8
  store %struct.Value* %0, %struct.Value** %3, align 8
  store %struct.Value* %1, %struct.Value** %4, align 8
  %5 = load %struct.Value*, %struct.Value** %3, align 8
  %6 = load %struct.Value*, %struct.Value** %4, align 8
  %7 = call %struct.Value* @_less_than(%struct.Value* %5, %struct.Value* %6)
  %8 = load %struct.Value*, %struct.Value** @_VFalse, align 8
  %9 = icmp ne %struct.Value* %7, %8
  br i1 %9, label %Block1, label %Block2
Block1:
  %10 = load %struct.Value*, %struct.Value** %3, align 8
  %11 = call %struct.Value* @_from_literal_int(i32 1)
  %12 = load %struct.Value*, %struct.Value** %3, align 8
  %13 = call %struct.Value* @_plus(%struct.Value* %11, %struct.Value* %12)
  %14 = load %struct.Value*, %struct.Value** %4, align 8
  %15 = call %struct.Value* @range(%struct.Value* %13, %struct.Value* %14)
  %16 = call %struct.Value* @_mk_pair(%struct.Value* %10, %struct.Value* %15)
  br label %Block3
Block2:
  %17 = load %struct.Value*, %struct.Value** @_VNull, align 8
  br label %Block3
Block3:
  %18 = phi %struct.Value* [ %16, %Block1 ], [ %17, %Block2 ]
  ret %struct.Value* %18
}


define %struct.Value* @sum(%struct.Value* %0) {
  %2 = alloca %struct.Value*, align 8
  store %struct.Value* %0, %struct.Value** %2, align 8
  %3 = load %struct.Value*, %struct.Value** %2, align 8
  %4 = call %struct.Value* @_nullp(%struct.Value* %3)
  %5 = load %struct.Value*, %struct.Value** @_VFalse, align 8
  %6 = icmp ne %struct.Value* %4, %5
  br i1 %6, label %Block1, label %Block2
Block1:
  %7 = call %struct.Value* @_from_literal_int(i32 0)
  br label %Block3
Block2:
  %8 = load %struct.Value*, %struct.Value** %2, align 8
  %9 = call %struct.Value* @_pair_car(%struct.Value* %8)
  %10 = load %struct.Value*, %struct.Value** %2, align 8
  %11 = call %struct.Value* @_pair_cdr(%struct.Value* %10)
  %12 = call %struct.Value* @sum(%struct.Value* %11)
  %13 = call %struct.Value* @_plus(%struct.Value* %9, %struct.Value* %12)
  br label %Block3
Block3:
  %14 = phi %struct.Value* [ %7, %Block1 ], [ %13, %Block2 ]
  ret %struct.Value* %14
}

The problem however is when we have a procedure defined within a procedure and the nested procedures accesses bindings from its parent. This is the same problem that one encounters with Pascal which has nested functions and procedures and more often implemented with stack frames.

What about allocating all of this on the heap? The benefit of using the heap is that we have a consistent mechanism for implementing closures which, to be honest, is the problem I'll be tacking straight after this one.