Dispatch techniques and closure representations

Similar documents
CSc 453 Interpreters & Interpretation

CSc 520 Principles of Programming Languages

CSc 453. Compilers and Systems Software. 18 : Interpreters. Department of Computer Science University of Arizona. Kinds of Interpreters

CSc 553. Principles of Compilation. 2 : Interpreters. Department of Computer Science University of Arizona

Today's Topics. CISC 458 Winter J.R. Cordy

Every language has its own scoping rules. For example, what is the scope of variable j in this Java program?

Tizen/Artik IoT Lecture Chapter 3. JerryScript Parser & VM

Optimizing Closures in O(0) time

point in worrying about performance. The goal of our work is to show that this is not true. This paper is organised as follows. In section 2 we introd

Short Notes of CS201

CSE 341: Programming Languages

CS201 - Introduction to Programming Glossary By

Scope, Functions, and Storage Management

Symbol Tables. ASU Textbook Chapter 7.6, 6.5 and 6.3. Tsan-sheng Hsu.

CS 265. Computer Architecture. Wei Lu, Ph.D., P.Eng.

VM instruction formats. Bytecode translator

About this exam review

Test 1 Summer 2014 Multiple Choice. Write your answer to the LEFT of each problem. 5 points each 1. Preprocessor macros are associated with: A. C B.

Principles of Programming Languages Topic: Scope and Memory Professor Louis Steinberg Fall 2004

CSE 504: Compiler Design. Runtime Environments

CS 360 Programming Languages Interpreters

Administration CS 412/413. Advanced Language Support. First-class vs. Second-class. First-class functions. Function Types

Concepts Introduced in Chapter 7

Jatha. Common Lisp in Java. Ola Bini JRuby Core Developer ThoughtWorks Studios.

CA Compiler Construction

CS577 Modern Language Processors. Spring 2018 Lecture Interpreters

Subroutines. Subroutine. Subroutine design. Control abstraction. If a subroutine does not fit on the screen, it is too long

Playing with bird guts. Jonathan Worthington YAPC::EU::2007

Lecture08: Scope and Lexical Address

Run-time Environments

Run-time Environments

Project. there are a couple of 3 person teams. a new drop with new type checking is coming. regroup or see me or forever hold your peace

G Programming Languages - Fall 2012

Context Threading: A flexible and efficient dispatch technique for virtual machine interpreters

Design Issues. Subroutines and Control Abstraction. Subroutines and Control Abstraction. CSC 4101: Programming Languages 1. Textbook, Chapter 8

CS 415 Midterm Exam Spring 2002

Programming in C++ 4. The lexical basis of C++

Chapter 11. Instruction Sets: Addressing Modes and Formats. Yonsei University

The role of semantic analysis in a compiler

Compilers. 8. Run-time Support. Laszlo Böszörmenyi Compilers Run-time - 1

INF 212 ANALYSIS OF PROG. LANGS FUNCTION COMPOSITION. Instructors: Crista Lopes Copyright Instructors.

Names, Scopes, and Bindings II. Hwansoo Han

Weeks 6&7: Procedures and Parameter Passing

CS64 Week 5 Lecture 1. Kyle Dewey

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

Parsing Scheme (+ (* 2 3) 1) * 1

Computer Architecture and Organization. Instruction Sets: Addressing Modes and Formats

Python Implementation Strategies. Jeremy Hylton Python / Google

Mechanized Operational Semantics

Project 2: Scheme Interpreter

Chapter 5. A Closer Look at Instruction Set Architectures. Chapter 5 Objectives. 5.1 Introduction. 5.2 Instruction Formats

Chapter 5. A Closer Look at Instruction Set Architectures

Jazelle ARM. By: Adrian Cretzu & Sabine Loebner

Run-time Environments - 2

For our next chapter, we will discuss the emulation process which is an integral part of virtual machines.

Example. program sort; var a : array[0..10] of integer; procedure readarray; : function partition (y, z :integer) :integer; var i, j,x, v :integer; :

Semantic Analysis. Outline. The role of semantic analysis in a compiler. Scope. Types. Where we are. The Compiler Front-End

CS1622. Semantic Analysis. The Compiler So Far. Lecture 15 Semantic Analysis. How to build symbol tables How to use them to find

Functions - Lecture 7. Nested functions. Another example. A Nested Function. Josef Svenningsson

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

CIT Week13 Lecture

Java: framework overview and in-the-small features

A Trace-based Java JIT Compiler Retrofitted from a Method-based Compiler

The basic operations defined on a symbol table include: free to remove all entries and free the storage of a symbol table

Where We Are. Lexical Analysis. Syntax Analysis. IR Generation. IR Optimization. Code Generation. Machine Code. Optimization.

Contents. Figures. Tables. Examples. Foreword. Preface. 1 Basics of Java Programming 1. xix. xxi. xxiii. xxvii. xxix

Late Binding; OOP as a Racket Pattern

Course introduction. Advanced Compiler Construction Michel Schinz

CS558 Programming Languages. Winter 2013 Lecture 3

INF 102 CONCEPTS OF PROG. LANGS FUNCTIONAL COMPOSITION. Instructors: James Jones Copyright Instructors.

CS 314 Principles of Programming Languages. Lecture 13

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

IMPLEMENTING PARSERS AND STATE MACHINES IN JAVA. Terence Parr University of San Francisco Java VM Language Summit 2009

Today's Topics. Last Time Modelling Scopes and Visibility The Run Stack, the Dynamic Pointer Stack, the Display (LL,ON) addressing

CSCI 171 Chapter Outlines

Chapter 9 Subprograms

CS 314 Principles of Programming Languages

High-Level Language VMs


Compilers. Type checking. Yannis Smaragdakis, U. Athens (original slides by Sam

CPSC 427: Object-Oriented Programming

CS 415 Midterm Exam Spring SOLUTION

Racket. CSE341: Programming Languages Lecture 14 Introduction to Racket. Getting started. Racket vs. Scheme. Example.

COMP 181. Agenda. Midterm topics. Today: type checking. Purpose of types. Type errors. Type checking

What about Object-Oriented Languages?

Chapter 5. A Closer Look at Instruction Set Architectures

NOTE: Answer ANY FOUR of the following 6 sections:

Lecture #16: Introduction to Runtime Organization. Last modified: Fri Mar 19 00:17: CS164: Lecture #16 1

Outline. Register Allocation. Issues. Storing values between defs and uses. Issues. Issues P3 / 2006

CS558 Programming Languages

Storage. Outline. Variables and Updating. Composite Variables. Storables Lifetime : Programming Languages. Course slides - Storage

Lambda Calculus see notes on Lambda Calculus

Compilers and Code Optimization EDOARDO FUSELLA

CSE 504. Expression evaluation. Expression Evaluation, Runtime Environments. One possible semantics: Problem:

Little Motivation Outline Introduction OpenMP Architecture Working with OpenMP Future of OpenMP End. OpenMP. Amasis Brauch German University in Cairo

Review of the C Programming Language

LECTURE 14. Names, Scopes, and Bindings: Scopes

Key Differences Between Python and Java

Compiler Construction

Chapter 1 GETTING STARTED. SYS-ED/ Computer Education Techniques, Inc.

Transcription:

Dispatch techniques and closure representations Jan Midtgaard Week 3, Virtual Machines for Programming Languages Aarhus University, Q4-2011

Dispatch techniques

Efficient bytecode interpreters (1/2) The bytecode interpreter spends all its time doing fetch-decode-execute Any microsecond saved in each iteration will result in significant speedups. 3 / 23

Efficient bytecode interpreters (2/2) There are several ways to write a byte-code interpreter: Switch-based interpreter Threaded interpreters Direct call threading Direct threading Indirect threading As we will see, some trade portability for performance 4 / 23

Switch-based interpreter (1/2) The classic implementation of a byte-code interpreter uses switch to dispatch: typedef enum { add /*... */ Inst; Inst program[] = { add /*... */ ; Inst* ip = program; void run() { while (1) { switch (*ip++) { case add: { int tmp1 = pop(); int tmp2 = pop(); push(tmp1 + tmp2); break; /*... */ 5 / 23

Switch-based interpreter (2/2) The classic implementation of a byte-code interpreter uses switch to dispatch: Pro: simple portable Con: slow void run() { while (1) { switch (*ip++) { case add: { int tmp1 = pop(); int tmp2 = pop(); push(tmp1 + tmp2); break; /*... */ 6 / 23

Call-threading interpreter (1/2) Alternatively, one can use function pointers as instructions. This dispatching technique is called (direct) call threading. typedef void (*Inst)(); void add() { int tmp1 = pop(); int tmp2 = pop(); push(tmp1 + tmp2); Inst* ip = program; void run() { while (1) { (*ip++)(); /*... */ Inst program[] = { add /*... */ ; 7 / 23

Call-threading interpreter (2/2) Alternatively, one can use function pointers as instructions. This dispatching technique is called (direct) call threading. Pro: Inst* ip = program; portable dispatch takes fewer instructions Con: void run() { while (1) { (*ip++)(); larger (instructions take more than a byte) 8 / 23

Towards a direct-threading interpreter This is sub-optimal: Each instruction always transfers control back to the main loop It does so by calling and returning iteratively If each instruction could just continue the rest of the computation by performing a tail call. (did someone say continuation-passing style?) 9 / 23

Direct-threading interpreter (1/2) Unfortunately C does not have proper tail calls, but we can use a GNU extension of C with labels as values : typedef void* Inst; void run() { Inst program[] = { &&add /*... */ ; Inst* ip = program; goto **ip++; add: { int tmp1 = pop(); int tmp2 = pop(); push(tmp1 + tmp2); goto **ip++; /*... */ 10 / 23

Direct-threading interpreter (2/2) Unfortunately C does not have proper tail calls, but we can use a GNU extension of C with labels as values : Pro: fast Con: less portable (GNU C, not ansi C) larger (instructions take more than a byte) void run() { Inst program[] = { &&add /*... */ ; Inst* ip = program; goto **ip++; add: { int tmp1 = pop(); int tmp2 = pop(); push(tmp1 + tmp2); goto **ip++; /*... */ 11 / 23

Indirect-threading interpreter (1/2) We can reduce the space requirements by one level of indirection. typedef enum { add /*... */ Inst; Inst program[] = { add /*... */ ; void run() { void* table[] = { &&add /*... */ ; Inst* ip = program; goto *table[*ip++]; add: { tmp1 = pop(); tmp2 = pop(); push(tmp1 + tmp2); goto *table[*ip++]; /*... */ 12 / 23

Indirect-threading interpreter (2/2) We can reduce the space requirements by one level of indirection. Pro: (relatively) fast Con: less portable (GNU C, not ansi C) void run() { void* table[] = { &&add /*... */ ; Inst* ip = program; goto *table[*ip++]; add: { tmp1 = pop(); tmp2 = pop(); push(tmp1 + tmp2); goto *table[*ip++]; /*... */ 13 / 23

Summary We ve seen four different techniques for writing a bytecode interpreter. Speedwise they rank roughly as follows: Direct call threading Switch-based interpreter Indirect threading (GNU C) Direct threading (GNU C) 14 / 23

Closure representations

Nested ironments and first-class functions In Java, the lexical ironment is coupled to the stack. On the JVM we can therefore refer to locals as stack offsets. In Pascal procedures can be nested, allowing us to refer to variables in an enclosing scope. In Scheme, ML and Lua where function values are first class, variables can even escape, i.e., outlive their stack frame. As a consequence, the ironment has to be decoupled from the stack. 16 / 23

Closures in the Scheme VM Closures are a standard representation for function values. From the DAIMIScheme VM specification: (load V x T j) Loads a Scheme value into T[j]. V can be:... close-flat for a flat closure (then x is the index of the abstraction and aux-vec holds a 1-element list containing the closure ironment) close-deep for a deep closure (then x is the index of the abstraction and the closure ironment is held by -lex) 17 / 23

A motivating example (define f (lambda (v w x y z) (let ([g (lambda () (let* ([u (car v)] [h (lambda () (let ((i (lambda () (+ w x y z 3)))) (cons i u)))]) h))]) g))) In the above example, the nested functions g, h, and i all refer to variables in the enclosing scope. Deep closures represent the nesting explicitly as a linked list of ironments. 18 / 23

Deep (or linked) closures (Landin 64) (define f (lambda (v w x y z) (let ([g (lambda () (let* ([u (car v)] [h (lambda () (let ((i (lambda () (+ w x y z 3)))) (cons i u)))]) h))]) g))) i s code h s code g s code u v w x y z 19 / 23

Deep (or linked) closures (Landin 64) Con: may create memory leaks (by keeping too many values live) Pro: simple fast to create outer variables in nested programs require search h s code i s code g s code u v w x y z 20 / 23

Flat (or display) closures (Cardelli 84) (define f (lambda (v w x y z) (let ([g (lambda () (let* ([u (car v)] [h (lambda () (let ((i (lambda () (+ w x y z 3)))) (cons i u)))]) h))]) g))) A flat closure gets a copy of its free variables: g s code h s code i s code v w x y z u w x y z w x y z 21 / 23

Flat (or display) closures (Cardelli 84) Flat closures come with a catch: Since each referring closure gets a copy of the value, an assignment to one copy will not be visible to other copies. This is typically handled by boxing: assigned variables are referred to through one level of indirection. g s code h s code i s code v w x y z u w x y z w x y z 22 / 23

Flat (or display) closures (Cardelli 84) Flat closures can be further optimized. For example, two mutually recursive functions can share the same flat ironment. g s code h s code i s code v w x y z u w x y z w x y z 22 / 23

Flat (or display) closures (Cardelli 84) Pro: Con: no memory leaks fast access more costly to create assigned variables require boxing g s code h s code i s code v w x y z u w x y z w x y z 22 / 23

Summary We ve seen two classical closure representations. There are advantages and disadvantages to both. The DAIMIScheme virtual machine supports both. The DAIMIScheme compiler uses a heuristic to choose between the two. As we will see later today, Lua uses a slightly more advanced variation. 23 / 23