Skip to content

Proposal for the Grant Program 2020 of Ruby Association

HASUMI Hitoshi edited this page Oct 11, 2020 · 3 revisions

Applicant name (individual or organization)

  • Monstar Lab, inc.

Contact person

  • Hasumi Hitoshi

Brief biography

  • A programmer of Monstar Lab
  • A contributor to https://github.com/mrubyc/mrubyc
  • Appeared in RubyKaigi Takeout 2020, 2019 and 2018, RubyConf 2019, RubyWorld Conference 2018
  • The first prize winner of Fukuoka Ruby Award 2020

Project title

  • mini mruby compiler (mmrbc)

Project summary

ja

mmrbc(mini mruby compiler)はRubyスクリプトをmrubyのVMコードへコンパイルする。256 KiB ROM, 128 KiB RAM程度のワンチップマイコンを動作環境として想定している。これは、mruby標準添付のmruby-compilerでは動作できない小さなリソースである。 小リソース環境向けのmruby VM実装であるmruby/cとmmrbcを組み合わせることにより、ワンチップマイコン上でRubyをインタプリタ実行できるようになる。

このシステムの用途は、コンパイルツールチェインなしで開発できるIoTデバイス入門キット、本格的な組み込みアプリケーション開発時の実機デバッグのサポート、Smalrubyと統合した学校教育向けセットアップなどが考えられる。

en

mmrbc (mini mruby compiler) compiles Ruby scripts into VM code of mruby. Typical operating environment is a one-chip microcontroller with 256 KiB ROM and 128 KiB RAM. It’s too small to run mruby-compiler that comes as a standard library with mruby. By combining mmrbc and mruby/c which is another mruby VM implementation for small resource environments, Ruby can run as an interpreter on a one-chip microcontroller.

Possible uses for this system include an IoT device introductory kit that can be developed without a compilation tool chain, facilitating debugging of the actual device when developing a full-scale embedded application, and a setup for school education integrated with Smalruby.

Project details

Outline

  • mmrbc is a small mruby compiler which runs on a one-chip microcontroller

Background

  • Reference board of mmrbc is PSoC5LP, specifications are as follows:
    • Arm 32bit architecture
    • 256 KiB ROM
    • 64 KiB RAM
    • It is also a reference board of mruby/c
  • mruby/c is a small VM implementation of mruby dedicated to one-chip microcontroller
    • It assumes that VM code is compiled by Matsumoto Yukihiro-san(Matz)'s original mruby compiler (mrbc)
  • It would be nice if one could execute Ruby scripts on a running microcontroller without pre-compiling
    • Imagine developing an IoT project with mruby/c:
      • 🎲 Changing a parameter of an analogue sensor on the fly would be very useful to find the proper configuration
      • 👀 Looking at values of @ivar and $gvar could make debugging easier
      • 📶 Sending arbitrary strings to a communication module could be helpful to check reachability
    • However, flashing both mrbc and mruby/c onto 256 KiB ROM is difficult to realize
  • This is the motivation of making a mini mruby compiler

Features of mmrbc

  • Portable and less dependent
    • mmrbc doesn't depend on mruby nor mruby/c
    • It can be built with only standard C library (libc)
    • libc can be not only glibc but also Newlib and Newlib-nano
  • Small footprint (by linking to Newlib-nano)
    • Fits in less than 256 KiB ROM (including mruby/c VM and some app code)
    • Runs on less than 64 KiB RAM
  • Selectable malloc and free
    • mmrbc is essentially independent from mruby/c though, malloc and free can be replaced with mrbc_alloc and mrbc_free which are implemented in mruby/c VM so that mmrbc can be integrated into mruby/c
      • (I call the integration "mmruby")
  • LEMON as a parser generator
    • It is originally a parser generator of SQLite
    • Nowadays, YACC/BISON is a typical choice of parser generator. Using LEMON might be unique in terms of implementing the Ruby language

Comparison between mrbc and mmrbc regarding RAM consumption

In this section, both mrbc and mmrbc are built for 64-bit architecture with glibc and they use Linux file system so that we can simply compare them by using valgrind.

  • mmrbc also uses glibc's malloc and free instead of mrbc_alloc and mrbc_free
  • The branch of mruby is mruby3 as of August 14th, 2020
  • Analysis command looks like: valgrind --tool=massif --stacks=yes path/to/(mrbc|mmrbc) source.rb
  • The results shown below are larger than the results on 32-bit architecture because the size of the pointer is twice as large in 64-bit architecture

Case 1: Compiling Hello World

Source code: hello.rb

puts "Hello World!"

Output:

Hello World!
Result of mrbc:
--------------------------------------------------------------------------------
Command:            ../mruby/build/host/bin/mrbc ./hello.rb
Massif arguments:   --stacks=yes
ms_print arguments: massif.out.4087
--------------------------------------------------------------------------------
    KB
157.8^                                                                       #
     |                                                                       #
     |                                                                       #
     |                                                                       #
     |                                                                       #
     |                                                                      @#
     |                                                                      @#
     |                                                                      @#
     |                                                                    @@@#
     |                                               @@:::::::::::@:::::::@ @#
     |                                       @:::::::@ :::: :: :: @: :: ::@ @#
     |                            :::::@:::::@: :: ::@ :::: :: :: @: :: ::@ @#
     |       ::::::::::::::::::::::: : @:: ::@: :: ::@ :::: :: :: @: :: ::@ @#
     |     ::::: ::: ::: ::: : : ::: : @:: ::@: :: ::@ :::: :: :: @: :: ::@ @#
     |     : ::: ::: ::: ::: : : ::: : @:: ::@: :: ::@ :::: :: :: @: :: ::@ @#
     |     : ::: ::: ::: ::: : : ::: : @:: ::@: :: ::@ :::: :: :: @: :: ::@ @#
     |     : ::: ::: ::: ::: : : ::: : @:: ::@: :: ::@ :::: :: :: @: :: ::@ @#
     |     : ::: ::: ::: ::: : : ::: : @:: ::@: :: ::@ :::: :: :: @: :: ::@ @#
     |     : ::: ::: ::: ::: : : ::: : @:: ::@: :: ::@ :::: :: :: @: :: ::@ @#
     |     : ::: ::: ::: ::: : : ::: : @:: ::@: :: ::@ :::: :: :: @: :: ::@ @#
   0 +----------------------------------------------------------------------->Mi
     0                                                                   3.235
Result of mmrbc:
--------------------------------------------------------------------------------
Command:            ../mmruby/build/host-production/bin/mmrbc hello.rb
Massif arguments:   --stacks=yes
ms_print arguments: massif.out.1652
--------------------------------------------------------------------------------
    KB
11.17^                                                                #       
     |                                                               @#       
     |                                                              @@#  ::   
     |                                                              @@#:::::  
     |                                            @     @  :@: : :: @@#:::::  
     |                                            @  :::@: :@::: :::@@#:::::  
     |                                            @  :::@: :@:::@:::@@#:::::  
     |                                            @:::::@:::@:::@:::@@#:::::  
     |                                            @: :::@:::@:::@:::@@#:::::  
     |      ::                                    @: :::@:::@:::@:::@@#:::::: 
     |      ::                                    @: :::@:::@:::@:::@@#:::::: 
     |      :: :                                  @: :::@:::@:::@:::@@#:::::: 
     |      :: :                                  @: :::@:::@:::@:::@@#:::::: 
     |      ::::                                  @: :::@:::@:::@:::@@#:::::: 
     |      ::::                                  @: :::@:::@:::@:::@@#:::::: 
     |      ::::                                  @: :::@:::@:::@:::@@#:::::: 
     |      ::::                                  @: :::@:::@:::@:::@@#:::::: 
     |      ::::                                  @: :::@:::@:::@:::@@#:::::: 
     |      ::::       ::::::::::::::: @:::::@::: @: :::@:::@:::@:::@@#:::::: 
     |     ::::::::::::: :: : :::::::::@:::::@::: @: :::@:::@:::@:::@@#:::::@ 
   0 +----------------------------------------------------------------------->ki
     0                                                                   224.5

Case2: Compiling larger Ruby script

Source: larger_script.rb

ary = Array.new(3)
ary[0] = {a: 123e2}
ary[0][:key] = "string"
ary[1] = %w(abc ABC Hello)
ary[2] = 0x1f
ary[3] = !true
puts "my name is #{self.class}, result is #{ary[2] * ary[0][:a]}"
p ary

Output:

my name is Object, result is 381300
[{:a=>12300, :key=>"string"}, ["abc", "ABC", "Hello"], 31, false]

(This example also demonstrates what syntax mmrbc can already compile)

Result of mrbc:
--------------------------------------------------------------------------------
Command:            ../mruby/build/host/bin/mrbc ./larger_script.rb
Massif arguments:   --stacks=yes
ms_print arguments: massif.out.4133
--------------------------------------------------------------------------------
    KB
162.9^                                                                     ## 
     |                                                                     #  
     |                                                                    @#  
     |                                                                    @#  
     |                                                                    @#  
     |                                                                    @#  
     |                                                                  @:@#  
     |                                                                  @:@#  
     |                                                                  @:@# :
     |                                                    :::@@::::::@@@@:@# :
     |                                     :::::::@::::::::::@ :: :::@  @:@# :
     |                          ::::@@:::::@ :: ::@:: ::: :::@ :: :::@  @:@# :
     |               ::::::::::::: :@ :::: @ :: ::@:: ::: :::@ :: :::@  @:@# :
     |     ::::::::::::: :: :: ::: :@ :::: @ :: ::@:: ::: :::@ :: :::@  @:@# :
     |     :: :::: ::::: :: :: ::: :@ :::: @ :: ::@:: ::: :::@ :: :::@  @:@# :
     |     :: :::: ::::: :: :: ::: :@ :::: @ :: ::@:: ::: :::@ :: :::@  @:@# :
     |     :: :::: ::::: :: :: ::: :@ :::: @ :: ::@:: ::: :::@ :: :::@  @:@# :
     |     :: :::: ::::: :: :: ::: :@ :::: @ :: ::@:: ::: :::@ :: :::@  @:@# :
     |     :: :::: ::::: :: :: ::: :@ :::: @ :: ::@:: ::: :::@ :: :::@  @:@# :
     |     :: :::: ::::: :: :: ::: :@ :::: @ :: ::@:: ::: :::@ :: :::@  @:@# :
   0 +----------------------------------------------------------------------->Mi
     0                                                                   3.470
Result of mmrbc:
--------------------------------------------------------------------------------
Command:            ../mmruby/build/host-production/bin/mmrbc larger_script.rb
Massif arguments:   --stacks=yes
ms_print arguments: massif.out.13414
--------------------------------------------------------------------------------
    KB
53.20^                                                                 #      
     |                                                                 #      
     |                                                                @#  ::: 
     |                                                                @#  : : 
     |                                                                @#  : : 
     |                                                               @@#  : : 
     |                                                               @@# :: : 
     |                                                               @@#::: : 
     |                                                               @@#::: : 
     |                                                           @@::@@#::: : 
     |                                                           @@: @@#::: : 
     |                                               @@:@:::@::::@@: @@#::: ::
     |                                         @::@::@ :@:: @:: :@@: @@#::: ::
     |                                 @:::@@@:@::@: @ :@:: @:: :@@: @@#::: ::
     |                           @@:@::@:: @@ :@::@: @ :@:: @:: :@@: @@#::: ::
     |                       ::@@@ :@: @:: @@ :@::@: @ :@:: @:: :@@: @@#::: ::
     |             :::::: :::::@ @ :@: @:: @@ :@::@: @ :@:: @:: :@@: @@#::: ::
     |      :::::@@:: :::::: ::@ @ :@: @:: @@ :@::@: @ :@:: @:: :@@: @@#::: ::
     |      : :: @ :: :::::: ::@ @ :@: @:: @@ :@::@: @ :@:: @:: :@@: @@#::: ::
     |      : :: @ :: :::::: ::@ @ :@: @:: @@ :@::@: @ :@:: @:: :@@: @@#::: ::
   0 +----------------------------------------------------------------------->Mi
     0                                                                   1.616

Conclusion (so far)

Ruby script mrbc mmrbc
hello.rb 157.8 KiB 11.17 KiB
larger_script.rb 162.9 KiB 53.20 KiB
  • mrbc:
    • It seems to be difficult to run on 64 KiB RAM even if the platform is a 32bit architecture
  • mmrbc:
    • It has a good chance to run on 64 KiB RAM if Ruby script is not big
    • The number of "instructions executed" tends to increase significantly as Ruby script grows
      • Because some internal structure like AST may not be optimal, I guess I can improve it

Current status of mmrbc

Scope of the project for the grant

  • Checking all unchecked checkboxes on https://github.com/hasumikin/mmruby/pull/6
    • Includes:
      • Conditional syntax represents OP_JMP OP_JMPIF, etc.
      • Nested IREPs generated by class MyClass; end def my_method(arg); end lambda and Proc, etc.
  • Trying to find a better internal structure to reduce RAM consumption

Out of scope

  • The complete syntax of mruby, such as:
    • The syntax which mruby/c VM doesn't cover, such as:
      • module
      • Multiple assignments, etc.
    • The syntax which seems to be unnecessary for one-chip microcontroller
      • Here document, etc.
    • Furthermore, some incompatibilities are acceptable as to keep mmrbc small enough
  • VM code optimization
    • eg) a = 4 can be compiled into OP_LOAD_4 R1(a)
      • instead, mmrbc always verbosely compiles into OP_LOAD_4 R2; OP_MOVE R1(a) R2

Above features will possibly be implemented as selectable options by compile flags in the future.

Project deliverables

Terminology

  • mrbc - Original mruby compiler written by Matz
  • mmrbc - Mini mruby compiler which I am writing and what I want to be accepted for the Grant Program 2020
  • mmruby - A language processing system integrating mmrbc and mruby/c
  • mruby machine - Integration of mmrbc, mruby/c, shell and peripheral drivers runs on one-chip microcontroller

Note:

  • I adopted the MIT License for this project, but it might have to be changed into BSD-3-Clause License due to mruby/c's current license