640 likes | 729 Views
Virgil Objects on the Head of a Pin. Ben L. Titzer UCLA Compilers Group titzer@cs.ucla.edu. The Microcontroller Challenge. ~6 billion MCUs produced annually In everything from microwaves to sensor nets Goal: Build robust and efficient systems software for very small devices
E N D
VirgilObjects on the Head of a Pin Ben L. Titzer UCLA Compilers Group titzer@cs.ucla.edu
The Microcontroller Challenge • ~6 billion MCUs produced annually • In everything from microwaves to sensor nets • Goal: Build robust and efficient systems software for very small devices • Robust: language-level safety • Efficient: fit in tiny memories, run fast • On the bare metal: device drivers + OS MCU: Atmel Atmega128 Flash: 128KB RAM: 4KB Devices: Timers, EEPROM, ADC, UART Node: Mica2 Dot Radio: CC1000 External Flash: 512KB Sensors: light, temp, acoustic 3 LEDs, serial port http://compilers.cs.ucla.edu/virgil
Difficulties • Domain constraints • Severe resource limitations • Low-level hardware details • Often lacks hardware protection • Events, interrupts, reentrancy • Energy, real-time requirements • Heavyweight runtime systems • Complex language services • Metadata requirements of language features http://compilers.cs.ucla.edu/virgil
104 101 102 103 105 106 107 108 109 The Size Gap data Most runtime Java data runtime C++ data libraries data C code microcontrollers embedded desktop http://compilers.cs.ucla.edu/virgil
104 101 102 103 105 106 107 108 109 The Size Gap data Most Virgil runtime Java C++ data libraries data C code microcontrollers embedded desktop http://compilers.cs.ucla.edu/virgil
IR IR Heap Heap Virgil Contributions Heap-specific optimization: RMA + RC + ROM Lightweight features Source Code IR optimization initialization Compiler Program initialization phase exe http://compilers.cs.ucla.edu/virgil
Virgil Basics • Overall goal: strong type safety • Familiar, well-defined primitives: int, boolean, char • Bounds-checked arrays of any type T as T[] • Dynamically safety checks for null and casts • With expressive features • Objects, components, arrays and delegates • Raw types 1…64 and operators for bit twiddling • Support for hardware access and interrupt handlers • But efficient implementation • No heavyweight runtime system • Straightforward object implementation • Sophisticated compiler optimizations http://compilers.cs.ucla.edu/virgil
Components • Components encapsulate global state • Those members that would be static in Java • Require no metadata • Like a TinyOS component, but without wiring • Serve as the unit of compile-time initialization • Can define hardware interrupt handlers componentTimer { fielddivider: int; methodstart() { . . . } methodstop() { . . . } methodinterrupt() { if (count % divider == 0) { LED.toggle(); count = 0; } } } http://compilers.cs.ucla.edu/virgil
Lightweight Objects • Virgil has classes similar to Java • Pass by reference semantics • Static types, dynamic safety checks • Single inheritance between classes • No interfaces • No java.lang.Object equivalent • Reduces size of metaobjects and object headers • Affords more programmer control • Complicates code reuse http://compilers.cs.ucla.edu/virgil
Delegates (C#) • Delegates are typed method references • Bound to a method and its object • Approximates a closure • Syntax generalizes expr.method() • Anonymous type: function(Ta): Tb • Precludes method overloading • Implemented as <object, method> tuple • No memory allocation (unlike C#) • Efficient dispatch strategy • Can be represented as scalars locally http://compilers.cs.ucla.edu/virgil
Delegate Example classList { fieldhead: Link; methodadd(i: Item) { . . . } methodapply(f: function(Item)) { local pos = head; while ( pos != null ) { f(pos.item); pos = pos.next; } } } componentClient { methodprintAll(l: List) { l.apply(print); } methodcopy(a: List, b: List) { a.apply(b.add); } methodprint(i: Item) { . . . } } Delegate type Invocation Delegates http://compilers.cs.ucla.edu/virgil
Raw Types and Operators • Bit twiddling is tedious • Unnaturally sized fields, bit layouts • Masking and shifting is error prone • Raw types model bit-level values • The type k in { 1…64 } is a k-bit value • Bitwise operators & ^ | << >> model width • The [] operator accesses bits like an array • 0x0FF and 0b01110 literals have width • Can convert integers, characters, booleans • Hardware registers have raw types http://compilers.cs.ucla.edu/virgil
Initialization Phase http://compilers.cs.ucla.edu/virgil
Initialization Phase • Virgil has no dynamic memory allocation • Compile-time application constructors • Application allocates objects, arrays • Configure parameters, initialize data structures • Turing-complete computation • Heap compiled into program binary • Available immediately at runtime • Further allocation disallowed • Sophisticated space optimizations http://compilers.cs.ucla.edu/virgil
Initialization Example programExample { components {Strobe,Timer,LED}; entrypoint main = Strobe.entry; entrypoint timer0_int = Timer.interrupt; } componentStrobe { fieldtree: Tree = new Tree(); constructor() { tree.add(6, 9, 11, 300); . . .; tree.balance(); } methodentry() { Timer.start(); } } componentTimer { fieldcount: int; methodstart() { . . . } methodinterrupt() { if ( Main.tree.find(count++) ) LED.toggle(); } } Main program declares included components and entrypoints Components contain initialization code for the program http://compilers.cs.ucla.edu/virgil
Optimization http://compilers.cs.ucla.edu/virgil
Compilation Techniques • Standard optimizations • Devirtualization, inlining, constant propagation • Orphan classes: require no metadata • Reachable Members Analysis: remove dead code, data, and fields • Reference Compression: compress objects by exploiting type safety http://compilers.cs.ucla.edu/virgil
Code Reuse or Code Refuse? • Drivers, libraries, kernels are general • Unused states and modes • Unused data structures, methods, fields • Examples: timer, doubly linked list • RAM extremely precious on MCU • Goal: include only used code+data • Remove dead code, fields, objects • Minimize overall footprint for program • Reduce program and heap http://compilers.cs.ucla.edu/virgil
Traditional Approach • Compiler explores call graph • Start at entrypoint method(s) • Transitively include called methods • Find dead fields by analyzing code • Remove dead fields from objects • Requires call site approximation • CHA, RTA, flow analysis • Do not leverage heap-specific information • Liveness cycle problem http://compilers.cs.ucla.edu/virgil
Example Program program heap componentMain { fieldf: A = new A(); fieldg: A = new B(); fieldh: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA { fieldz: int = 19; methodm(): A { return this; } } classB extends A { methodm(): A { return Main.g; } } classC extends A { methodm(): A { z++; return this; } } Main: Main { fieldf: A = A1; fieldg: A = B1; fieldh: A = C1; } A1: A { fieldz: int = 19; } B1: B { fieldz: int = 19; } C1: C { fieldz: int = 19; } http://compilers.cs.ucla.edu/virgil
Reachable Members Analysis • Given: entrypoint methods and live heap • Assume everything is dead until used • Call graph approx. considers only live objects • Can assume no dynamic memory allocation, or conservatively include allocation sites • More precise than CHA, RTA analyses • Objects and types considered on demand • Avoids liveness cycle http://compilers.cs.ucla.edu/virgil
RMA Illustration heap program component Main { field f: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (1) heap program componentMain { field f: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (2) heap program componentMain { field f: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (3) heap program componentMain { field f: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } Use of Main.f B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (4) heap program componentMain { field f: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (5) heap program componentMain { field f: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } Use of A.m B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (6) heap program componentMain { field f: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (7) heap program componentMain { field f: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (8) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (9) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (10) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } class A { field z: int = 19; method m(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (11) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { field f: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (12) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (13) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (14) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; field g: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (15) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; field g: A = B1; field h: A = C1; } A1:A{ field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (16) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; field g: A = B1; field h: A = C1; } A1:A{ field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (17) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A { return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; field g: A = B1; field h: A = C1; } A1:A{ field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (18) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A{ return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; field g: A = B1; field h: A = C1; } A1:A{ field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
current RMA Illustration (19) heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A{ return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; field g: A = B1; field h: A = C1; } A1:A{ field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
RMA Result heap program componentMain { fieldf: A = new A(); field g: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A{ return this; } } class B extends A { method m(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; field g: A = B1; field h: A = C1; } A1:A{ field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
CHA Result heap program componentMain { fieldf:A = new A(); fieldg: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ fieldz: int = 19; methodm(): A { return this; } } classBextends A { methodm(): A { return Main.g; } } classCextends A { methodm(): A { z++; return this; } } Main: Main { fieldf: A = A1; fieldg: A = B1; field h: A = C1; } A1: A { fieldz: int = 19; } B1: B { fieldz: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
RTA Result heap = { A, B, C } program componentMain { fieldf:A = new A(); fieldg: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ fieldz: int = 19; methodm(): A { return this; } } classBextends A { methodm(): A { return Main.g; } } classCextends A { methodm(): A { z++; return this; } } Main: Main { fieldf: A = A1; fieldg: A = B1; field h: A = C1; } A1: A { fieldz: int = 19; } B1: B { fieldz: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
Final RTA Result heap = { A, B } program componentMain { fieldf:A = new A(); fieldg: A = new B(); field h: A = new C(); methodentry() { while ( true ) f = f.m(); } } classA{ field z: int = 19; methodm(): A { return this; } } classBextends A { methodm(): A { return Main.g; } } class C extends A { method m(): A { z++; return this; } } Main: Main { fieldf: A = A1; fieldg: A = B1; field h: A = C1; } A1: A { field z: int = 19; } B1: B { field z: int = 19; } C1: C { field z: int = 19; } http://compilers.cs.ucla.edu/virgil
Reference Compression • Pointers normally 16-bit integers • Types restrict referencible sets • Reference of type A can only point to A objects • Complete heap available after initialization • Represent references specially • Compression table approach • Use object handles instead of direct pointers • Handle tables can be stored in ROM http://compilers.cs.ucla.edu/virgil
A X B C Y Z M D E N P Q Referencible Sets Object Virgil has no universal superclass http://compilers.cs.ucla.edu/virgil
A X B C Y Z M D E N P Q Referencible Sets Disjoint hierarchies completed unrelated http://compilers.cs.ucla.edu/virgil
A X B C Y Z M D E N P Q Referencible Sets Each can be considered independently http://compilers.cs.ucla.edu/virgil
A X B C Y Z M D E N P Q Referencible Sets Type system restricts referencible sets http://compilers.cs.ucla.edu/virgil