# μRust: A Simple Rust Programming Language **Compiler 2023 Programming Assignment III Compiler for Java Assembly Code Generation Due Date: 2023/06/16** [Lab3 link](https://classroom.github.com/assignment-invitations/d1e9fb450f3ab4328182a184a188b8f3) This assignment is to generate Java assembly code (for Java Virtual Machines) of the given μRust program. The generated code will then be translated to the Java bytecode by the Java assembler, Jasmin. The generated Java bytecode should be run by the Java Virtual Machine (JVM) successfully. * Environmental Setup * OS: Ubuntu 20.04 LTS * Install dependencies: ```$ sudo apt install flex bison``` * Java Virtual Machine (JVM): ```$ sudo apt install default-jre``` * Java Assembler (Jasmin) is included in the Compiler hw3 le. * Judgmental tool: ```$ pip3 install local-judge``` ## Java Assembly Code Generation In this assignment, you have to build a μRust compiler. The descriptions for the execution steps are as follows. 1. Build your μRust compiler by injecting the Java assembly code into your flex/bison code developed in the previous assignments. 2. Run the compiler with the given μRust program (e.g., test.rs le) to generate the corresponding Java assembly code (e.g., test.j le). 3. Run the Java assembler, Jasmin, to convert the Java assembly code into the Java bytecode (e.g., test.class le). 4. Run the generated Java bytecode (e.g., test.class le) with JVM and display the results. ## Workflow Of The Assignment You are required to build a μRust compiler based on the previous two assignments. The execution steps are described as follows. 1. Build your compiler by make command and you will get an executable named mycompiler . 2. Run your compiler using the command ```$ ./mycompiler < input.rs``` , which is built by lex and yacc, with the given μRust code ( .rs le) to generate the corresponding Java assembly code ( .j le). 3. The Java assembly code can be converted into the Java Bytecode ( .class le) through the Java assembler, Jasmin, i.e., use ```$ java -jar jasmin.jar hw3.j``` to generate Main.class . 4. Run the Java program ( .class le) with Java Virtual Machine (JVM); the program should generate the execution results required by this assignment, i.e., use ```$ java Main``` to run the executable. ## What Should Your Compiler Do? In Assignment 3, the flex/bison only need to print out the error messages, we score your assignment depending on the JVM execution result, i.e., the output of the command: ```$ java Main .``` When ERROR occurs during the parsing phase, we expect your compiler to print out ALL error messages, as Assignment 2 did, and **DO NOT** generate the Java assembly code (.j le). Each test case is 7pt, and you can check the correctness by local-judge (type judge command in your terminal) as hw1 and hw2. ## Submission We use GitHub Classroom to collect assignments from students. Push your code to Github before the deadline. ## Demonstration of Your Assignment 3 The form and schedule of demonstration will be announced on Moodle later. During the demonstration, you will be asked to demonstrate your assignment downloaded from Moodle and you need to answer the questions about the logics of your codes in 5 ~ 10 minutes. Note that TA will verify that your compiler is not hardcoded to the attached inputs and outputs. For the hardcoded case, you will get 0pt. ## Reference * Jasmin instructions: http://jasmin.sourceforge.net/instructions.html * Java bytecode instruction listings: https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings * Java Language and Virtual Machine Specications: https://docs.oracle.com/javase/specs/ * The Rust (not μRust) Playground: https://play.rust-lang.org/ ## Appendix (Jasmin Instructions) In this section, we list the Jasmin instructions that you may use in developing your compiler. Please note that the assignment are not limited to using the commands and approaches introduced below. ### Setup Code A valid Jasmin program should include the code segments for the execution environment setup. Your compiler should be able to generate the setup code, together with the translated Jasmin instructions (as shown in the previous paragraphs). The example code is listed as below. ```jasmin .source hw3.j .class public Main .super java/lang/Object ; ... Your generated Jasmin code for the input μRust program ... .method public static main([Ljava/lang/String;)V ; main function .limit stack 100 ; Define your storage size. .limit locals 100 ; Define your local space number. ; ... Your generated Jasmin code for the input μRust program ... return .end method ``` ### Literals | Constant in μRust | Jasmin Instruction | | -------- | -------- | | 25 | ldc 25 | | 3.14 | ldc 3.14 | | "Hi" | ldc "Hi" | | true / false | iconst_1 / iconst_0 | ### Print The following example shows how to print out the constants with the Jasmin code. Note that there is a little bit dierent for the actual parameters of the println functions invoked by the invokevirtual instructions, i.e., i32 ( I ), f32 ( F ), and string ( Ljava/lang/String; ). Note also that you need to treat bool type as string when encountering print statement, and the corresponding code segments are shown as below. * μRust Code ```rust println(30) print("Hello") ``` * Jasmin Code (for reference only) ```jasmin ldc 30 ; integer getstatic java/lang/System/out Ljava/io/PrintStream; swap invokevirtual java/io/PrintStream/println(I)V ldc "Hello" ; string getstatic java/lang/System/out Ljava/io/PrintStream; swap invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V ``` ### Operations #### Unary Operators | μRust Operator | Jasmin Instruction(i32) | Jasmin Instruction(f32) | | -------- | -------- | -------- | | + | (ignore) | (ignore) | | - | ineg | fneg | #### Binary Operators | μRust Operator | Jasmin Instruction(i32) | Jasmin Instruction(f32) | | -------- | -------- | -------- | | + | iadd | fadd | | - | isub | fsub | | * | imul | fmul | | / | idiv | fdiv | | % | irem | - | #### Bitwise Operators | μRust Operator | Jasmin Instruction | | -------- | -------- | | & | iand | | \| | ior | | ^ | ixor | | << | ishl | | >> | iushr | The following example shows the standard unary and binary arithmetic operations in μRust and the corresponding Jasmin instructions. * μRust Code ```rust -5 + 3 * 2; ``` * Jasmin Code (for reference only) ```jasmin ldc 5 ineg ldc 3 ldc 2 imul iadd ``` ### Store/Load Variables The following example shows how to load the constant at the top of the stack and store the value to the local variable ( x = 9; ). In addition, it then loads a constant to the Java stack, loads the content of the local variable, and adds the two values before the results are stored to the local variable ( y = 4 + x; ). Furthermore, the example code exhibits how to store a string to the local variable ( z = "Hello"; ). The contents of local variables after the execution of the Jasmin code are shown as below. * μRust Code ```rust x = 9; y = 4 + x; z = "Hello"; ``` * Jasmin Code (for reference only) ```jasmin ldc 9 istore 0 ; store 9 to x ldc 4 iload 0 ; load x iadd ; add 4 and x istore 1 ; store the result to y ldc "Hello" astore 2 ; store a string to z ``` ### Jump Instruction | Jasmin Instruction | Description | | -------- | -------- | | goto <label> | direct jump | | ifeq <label> | jump if zero | | ifne <label> | jump if nonzero | | iflt <label> | jump if less than zero | | ifle <label> | jump if less than or equal to zero | | ifgt <label> | jump if greater than zero | | ifge <label> | jump if greater than or equal to zero | * μRust Code ```rust if x == 10 { /* do something */ } else { /* do the other thing */ } ``` * Jasmin Code (for reference only) ```jasmin iload 0 ; load x ldc 10 ; load integer 10 isub ifeq L_cmp_0 ; jump to L_cmp_0 if x == 0 ; if not, execute next line iconst_0 ; false (if x != 0) goto L_cmp_1 ; skip loading true to the stack ; by jumping to L_cmp_1 L_cmp_0: ; if x == 0 jump to here iconst_1 ; true L_cmp_1: ifeq L_if_false ; -> do something goto L_if_exit L_if_false: ; -> do the other thing L_if_exit: ``` ### Type Conversions The following example shows the usage of the casting instructions, i2f and f2i , where x is i32 local variable 0. * μRust Code ```rust x = x + 6.28 as i32; ``` * Jasmin Code (for reference only) ```jasmin iload 0 ldc 6.28 f2i iadd istore 0 ``` ### Method Invocation There are several forms of method-calling instructions in the JVM. In this homework, methods are called using the invokestatic instruction. The usage can be shown by following example. A function foo has signature (II)I that you have implemented in hw2, and this information is used during the code generation. The invokestatic Main/foo(II)I is used to invoke the method foo after two actual argumants ( 3 , 4 ) are loaded to the stack, and then the result of the function output will be pushed to the stack. * μRust Code ```rust fn foo(x: i32, y: i32) ->i32 { return x + y; } fn main() { let z: i32 = foo(3, 4); println(z); } ``` * Jasmin Code (for reference only) ```jasmin .method public static foo(II)I ; Define foo function .limit stack 20 .limit locals 20 iload 0 ; load the first argument iload 1 ; load the second argument iadd ireturn .end method .method public static main([Ljava/lang/String;)V .limit stack 100 .limit locals 100 ldc 3 ; push argument to the stack ldc 4 ; push argument to the stack invokestatic Main/foo(II)I ; invoke `foo` method in `Main` class istore 2 ; store the result to z iload 2 ; load z for println getstatic java/lang/System/out Ljava/io/PrintStream; swap invokevirtual java/io/PrintStream/println(I)V return .end method ```