Merge: lib/glesv2: intro framebuffer related services and try a new style
authorJean Privat <jean@pryen.org>
Wed, 11 Feb 2015 04:11:52 +0000 (11:11 +0700)
committerJean Privat <jean@pryen.org>
Wed, 11 Feb 2015 04:11:52 +0000 (11:11 +0700)
The main point of this PR is to try a new naming convention for the glesv2 module.

Ideas behind this convention:
1. Be as close as possible to the C API: use the same function and macro names.
2. Modify those names only for compatibility with the Nit language: the macros cannot begin with `GL_` so use `gl_` instead.
3. Apply types and other Nity features when they can help and don't interfere with the original API.

It differs from the previous convention of renaming all entities to the Nit style. It is not heavier in the module definition but should be easier for the users.

In practice, to convert OpenGL ES C code to Nit you would need to change the prefix `GL_` to `gl_`, and remove `;`.

In C:
~~~
glBindFramebuffer(GL_FRAMEBUFFER, my_fbo);
glBindRenderbuffer(GL_RENDERBUFFER, my_renderbuffer);
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
~~~

In Nit:
~~~
glBindFramebuffer(gl_FRAMEBUFFER, my_fbo)
glBindRenderbuffer(gl_RENDERBUFFER, my_renderbuffer)
return glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
~~~

What do you think of the new naming convention? I would convert the whole module to it.

Pull-Request: #1157
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

benchmarks/bench_engines.sh
misc/README.md
misc/vim/plugin/nit.vim
share/man/nitc.md
src/compiler/separate_compiler.nit
src/doc/vim_autocomplete.nit

index 6b43e55..571d71f 100755 (executable)
@@ -331,6 +331,26 @@ function bench_compilation_time
 }
 bench_compilation_time
 
+function bench_linkboost()
+{
+       name="$FUNCNAME"
+       skip_test "$name" && return
+       prepare_res "$name-nitc-st.dat" "nitc-st" "nitc with --separate --trampoline-call"
+       run_compiler "nitc-st" ./nitc --separate --trampoline-call
+       prepare_res "$name-nitc-s.dat" "nitc-s" "nitc with --separate"
+       run_compiler "nitc-s" ./nitc --separate
+       prepare_res "$name-nitc-sc.dat" "nitc-sc" "nitc with --separate --colors-are-symbols"
+       run_compiler "nitc-sc" ./nitc --separate --colors-are-symbols
+       prepare_res "$name-nitc-sct.dat" "nitc-sct" "nitc with --separate --colors-are-symbols --trampoline-call"
+       run_compiler "nitc-sct" ./nitc --separate --colors-are-symbols --trampoline-call
+       prepare_res "$name-nitc-sl.dat" "nitc-sl" "nitc with --separate --link-boost"
+       run_compiler "nitc-scts" ./nitc --separate --link-boost
+       prepare_res "$name-nitc-sg.dat" "nitc-sg" "nitc with --separate --semi-global"
+       run_compiler "nitc-sg" ./nitc --separate --semi-global
+       plot "$name.gnu"
+}
+bench_linkboost
+
 if test -n "$html"; then
        echo >>"$html" "</body></html>"
 fi
index 947112e..181c047 100644 (file)
@@ -81,7 +81,8 @@ The omnifunc applies a simple heuristic to recognize what kind of entities to di
 (This is a simplification some behaviors are missing.)
 
 * If the cursor follows `import`, it will list known modules.
-* If it follows `new`, `super` or `class` it will list known classes.
+* If it follows `new` it will list known classes with their constructors.
+* If it follows `super`, `class`, `isa` or `as` it will list known classes.
 * If it follows a `.`, it will list properties.
 * If on an extern method declaration, it will list classes and properties.
 * Otherwise, it will list keywords and properties.
index d58df14..23e557a 100644 (file)
@@ -139,7 +139,7 @@ fun NitOmnifuncAddAMatch(matches, words, name)
        let desc = get(a:words, 3, '')
        let desc = join(split(desc, '#nnnn#', 1), "\n")
        if strlen(pretty) > 40
-               let pretty = pretty[:37] . '...'
+               let pretty = pretty[:39] . '…'
        endif
        call add(a:matches, {'word': a:name, 'abbr': pretty, 'menu': synopsis, 'info': desc, 'dup': 1})
 endfun
@@ -188,12 +188,24 @@ fun NitOmnifunc(findstart, base)
                        call NitOmnifuncAddFromFile(a:base, matches, 'modules.txt')
                endif
 
-               " Classes
-               if count(['new', 'super', 'class', 'nullable'], prev_word) > 0 ||
+               " Classes (instanciable only)
+               if prev_word == 'new'
+                       let handled = 1
+                       call NitOmnifuncAddFromFile(a:base, matches, 'constructors.txt')
+               endif
+
+               " Other class references
+               if count(['class', 'super'], prev_word) > 0
+                       let handled = 1
+                       call NitOmnifuncAddFromFile(a:base, matches, 'classes.txt')
+               endif
+
+               " Types
+               if count(['class', 'super', 'nullable', 'isa', 'as'], prev_word) > 0 ||
                 \ line_prev_cursor =~ '[' || prev_char == ':' ||
                 \ (line_prev_cursor =~ 'fun' && line_prev_cursor =~ 'import' && (prev_word == 'import' || prev_char == ','))
                        let handled = 1
-                       call NitOmnifuncAddFromFile(a:base, matches, 'classes.txt')
+                       call NitOmnifuncAddFromFile(a:base, matches, 'types.txt')
                endif
 
                " Properties
index f98d50d..75da7c8 100644 (file)
@@ -274,6 +274,36 @@ will increase.
 :   Do not compile dead methods (semi-global).
     Need `--rta`.
 
+## LINK-BOOST OPTIMIZATIONS
+
+In `--separate` and in `--erasure` modes, some optimization can be gained by hijacking the linker process.
+
+Warning: these optimisations are not portable since they use extra features of the GNU linker `ld`.
+However, there is very few reasons to not use them if GNU `ld` is available.
+
+`--link-boost`
+:   Enable all link-boost optimizations.
+
+`--colors-are-symbols`
+:   Store colors as symbols instead of static data.
+
+    By default, the various identifiers used to implement OO-mechanisms are stored as genuine constant static variables.
+
+    This option uses linker symbols to encode these identifiers.
+    This makes the compiled program faster since less indirections are required to get the values.
+    It also produces executables that are a little bit smaller since static memory does not have to store the colors.
+
+`--substitute-monomorph`
+:   Replace monomorphic trampolines with direct call.
+
+    Late-binding is implemented with *trampolines*, that are small functions that just select and jump the to right implementations.
+    If, at link-time, is it known that the target will always by the same implementation then all calls to the trampoline are replaced by
+    direct calls to this single implementation.
+
+    Note that using trampolines as indirection slows down the executable.
+    However, it is expected that the gain of monomorphic direct-calls overcompensates the additional indirections in polymorphic trampoline-calls.
+
+    Note: automatically enable option `--trampoline-call`.
 
 ## DANGEROUS OPTIMIZATIONS
 
@@ -329,21 +359,13 @@ Usually you do not need them since they make the generated code slower.
 `--colo-dead-methods`
 :   Force colorization of dead methods.
 
-`--colors-are-symbols`
-:   Store colors as symbols instead of static data.
-
-    By default, the various identifiers used to implement OO-mechanisms are stored as genuine constant static variables.
-
-    This option uses linker symbols to encode these identifiers.
-    This makes the compiled program faster since less indirections are required to get the values.
-    It also produces executables that are a little bit smaller since static memory does not have to store the colors.
-
-    Warning: the usage of linker symbols is not portable on all toolchains (eg. Mac OS X).
-    Also, this option does nothing on some platforms (like android).
-
 `--no-gcc-directive`
 :   Disable advanced gcc directives for optimization.
 
+`--trampoline-call`
+:   Use an indirection when calling.
+
+    Just add the trampolines of `--substitute-monomorph` without doing any aditionnal optimizations.
 
 ## INTERNAL OPTIONS
 
index 9c03910..9010227 100644 (file)
@@ -29,8 +29,15 @@ redef class ToolContext
        var opt_no_union_attribute = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
        # --no-shortcut-equate
        var opt_no_shortcut_equate = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
+
        # --colors-are-symbols
-       var opt_colors_are_symbols = new OptionBool("Store colors as symbols (faster)", "--colors-are-symbols")
+       var opt_colors_are_symbols = new OptionBool("Store colors as symbols (link-boost)", "--colors-are-symbols")
+       # --trampoline-call
+       var opt_trampoline_call = new OptionBool("Use an indirection when calling", "--trampoline-call")
+       # --substitute-monomorph
+       var opt_substitute_monomorph = new OptionBool("Replace monomorph trampoline with direct call (link-boost)", "--substitute-monomorph")
+       # --link-boost
+       var opt_link_boost = new OptionBool("Enable all link-boost optimizations", "--link-boost")
 
        # --inline-coloring-numbers
        var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
@@ -53,7 +60,8 @@ redef class ToolContext
                self.option_context.add_option(self.opt_separate)
                self.option_context.add_option(self.opt_no_inline_intern)
                self.option_context.add_option(self.opt_no_union_attribute)
-               self.option_context.add_option(self.opt_no_shortcut_equate, opt_colors_are_symbols)
+               self.option_context.add_option(self.opt_no_shortcut_equate)
+               self.option_context.add_option(opt_colors_are_symbols, opt_trampoline_call, opt_substitute_monomorph, opt_link_boost)
                self.option_context.add_option(self.opt_inline_coloring_numbers, opt_inline_some_methods, opt_direct_call_monomorph, opt_skip_dead_methods, opt_semi_global)
                self.option_context.add_option(self.opt_colo_dead_methods)
                self.option_context.add_option(self.opt_tables_metrics)
@@ -70,6 +78,13 @@ redef class ToolContext
                        tc.opt_direct_call_monomorph.value = true
                        tc.opt_skip_dead_methods.value = true
                end
+               if tc.opt_link_boost.value then
+                       tc.opt_colors_are_symbols.value = true
+                       tc.opt_substitute_monomorph.value = true
+               end
+               if tc.opt_substitute_monomorph.value then
+                       tc.opt_trampoline_call.value = true
+               end
        end
 
        var separate_compiler_phase = new SeparateCompilerPhase(self, null)
@@ -561,6 +576,29 @@ class SeparateCompiler
                                r.compile_to_c(self)
                                var r2 = pd.virtual_runtime_function
                                if r2 != r then r2.compile_to_c(self)
+
+                               # Generate trampolines
+                               if modelbuilder.toolcontext.opt_trampoline_call.value then
+                                       r2.compile_trampolines(self)
+
+                                       # Replace monomorphic call to a trampoline by a direct call to the virtual implementation
+                                       if modelbuilder.toolcontext.opt_substitute_monomorph.value then do
+                                               var m = pd.mproperty
+                                               if rta == null then
+                                                       # Without RTA, monomorphic means alone (uniq name)
+                                                       if m.mpropdefs.length != 1 then break label
+                                               else
+                                                       # With RTA, monomorphic means only live methoddef
+                                                       if not rta.live_methoddefs.has(pd) then break label
+                                                       for md in m.mpropdefs do
+                                                               if md != pd and rta.live_methoddefs.has(md) then break label
+                                                       end
+                                               end
+                                               # Here the trick, GNU ld can substitute symbols with specific values.
+                                               var n2 = "CALL_" + m.const_color
+                                               linker_script.add("{n2} = {r2.c_name};")
+                                       end label
+                               end
                        end
                end
                self.mainmodule = old_module
@@ -1104,7 +1142,7 @@ class SeparateCompilerVisitor
                        return res
                end
 
-               return table_send(mmethod, arguments, mmethod.const_color)
+               return table_send(mmethod, arguments, mmethod)
        end
 
        # Handle common special cases before doing the effective method invocation
@@ -1170,7 +1208,7 @@ class SeparateCompilerVisitor
                return res
        end
 
-       private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
+       private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], mentity: MEntity): nullable RuntimeVariable
        do
                compiler.modelbuilder.nb_invok_by_tables += 1
                if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
@@ -1204,8 +1242,16 @@ class SeparateCompilerVisitor
                        ss.append(", {a}")
                end
 
-               self.require_declaration(const_color)
-               var call = "(({runtime_function.c_ret} (*){runtime_function.c_sig})({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
+               var const_color = mentity.const_color
+               var call
+               if not compiler.modelbuilder.toolcontext.opt_trampoline_call.value then
+                       self.require_declaration(const_color)
+                       call = "(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
+               else
+                       var callsym = "CALL_" + const_color
+                       self.require_declaration(callsym)
+                       call = "{callsym}({ss}) /* {mmethod} on {arguments.first.inspect}*/"
+               end
 
                if res != null then
                        self.add("{res} = {call};")
@@ -1281,7 +1327,7 @@ class SeparateCompilerVisitor
                        self.compiler.mainmodule = main
                        return res
                end
-               return table_send(m.mproperty, arguments, m.const_color)
+               return table_send(m.mproperty, arguments, m)
        end
 
        redef fun vararg_instance(mpropdef, recv, varargs, elttype)
@@ -1881,6 +1927,12 @@ class SeparateRuntimeFunction
                return sig.to_s
        end
 
+       # The C type for the function pointer.
+       var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
+
+       # The arguments, as generated by `compile_to_c`
+       private var arguments: Array[RuntimeVariable] is noinit
+
        redef fun compile_to_c(compiler)
        do
                var mmethoddef = self.mmethoddef
@@ -1917,6 +1969,7 @@ class SeparateRuntimeFunction
                        comment.append(": {ret}")
                end
                compiler.provide_declaration(self.c_name, "{sig};")
+               self.arguments = arguments.to_a
 
                v.add_decl("/* method {self} for {comment} */")
                v.add_decl("{sig} \{")
@@ -1942,6 +1995,50 @@ class SeparateRuntimeFunction
                v.add("\}")
                compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
        end
+
+       # Compile the trampolines used to implement late-binding.
+       #
+       # See `opt_trampoline_call`.
+       fun compile_trampolines(compiler: SeparateCompiler)
+       do
+               var recv = self.mmethoddef.mclassdef.bound_mtype
+               var selfvar = arguments.first
+               var ret = called_signature.return_mtype
+
+               if mmethoddef.is_intro and recv.ctype == "val*" then
+                       var m = mmethoddef.mproperty
+                       var n2 = "CALL_" + m.const_color
+                       compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
+                       var v2 = compiler.new_visitor
+                       v2.add "{c_ret} {n2}{c_sig} \{"
+                       v2.require_declaration(m.const_color)
+                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       if ret != null then
+                               v2.add "return {call}"
+                       else
+                               v2.add call
+                       end
+
+                       v2.add "\}"
+
+               end
+               if mmethoddef.has_supercall and recv.ctype == "val*" then
+                       var m = mmethoddef
+                       var n2 = "CALL_" + m.const_color
+                       compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
+                       var v2 = compiler.new_visitor
+                       v2.add "{c_ret} {n2}{c_sig} \{"
+                       v2.require_declaration(m.const_color)
+                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       if ret != null then
+                               v2.add "return {call}"
+                       else
+                               v2.add call
+                       end
+
+                       v2.add "\}"
+               end
+       end
 end
 
 redef class MEntity
index 2472466..856633f 100644 (file)
@@ -14,8 +14,9 @@
 
 # Generate files used by the Vim plugin to autocomplete with doc
 #
-# There is 3 files generated, each with a different target: modules, classes,
-# and properties. Each line describe a different entity, with 4 values:
+# There is 3 files generated, each with a different target: modules, types,
+# properties and constructors. Each line describe a different entity,
+# with 4 values:
 #
 # 1. Short name to use in autocompletion
 # 2. Full signature
 # 4. Full doc with extra
 #
 # The priority with those files is for them to be analyzed efficiently, for
-# this reason, the data is prepared in advant and some information may be
+# this reason, the data is prepared in advance and some information may be
 # duplicated.
 module vim_autocomplete
 
 import modelbuilder
 import phase
 import modelize::modelize_class
+import model_utils
 
 redef class ToolContext
        # Phase generating the files for the Vim plugin
@@ -53,16 +55,16 @@ redef class MEntity
        private fun write_to_stream(stream: OStream)
        do
                # 1. Short name for autocompletion
-               stream.write name
+               stream.write complete_name
                stream.write field_separator
 
                # 2. Full signature
-               stream.write name
+               stream.write complete_name
                write_signature_to_stream(stream)
                stream.write field_separator
 
                # 3. Doc synopsis
-               var mdoc = mdoc
+               var mdoc = complete_mdoc
                if mdoc != null then
                        stream.write mdoc.content.first
                end
@@ -81,6 +83,12 @@ redef class MEntity
        end
 
        private fun write_signature_to_stream(stream: OStream) do end
+
+       # Actual name used in completion
+       private fun complete_name: String do return name
+
+       # Doc to use in completion
+       private fun complete_mdoc: nullable MDoc do return mdoc
 end
 
 redef class MMethodDef
@@ -103,6 +111,43 @@ redef class MAttributeDef
        end
 end
 
+# Use `MClassDef` as anchor for its constructors only
+redef class MClassDef
+       private var target_constructor: nullable MMethodDef = null
+
+       redef fun complete_name
+       do
+               var target_constructor = target_constructor
+               assert target_constructor != null
+
+               var params
+               var mparameters = mclass.mparameters
+               if not mparameters.is_empty then
+                       params = "[{mparameters.join(", ")}]"
+               else
+                       params = ""
+               end
+
+               if target_constructor.name != "init" and target_constructor.name != "new" then
+                       return name + params + "." + target_constructor.name
+               end
+
+               return name + params
+       end
+
+       redef fun complete_mdoc
+       do
+               var target_constructor = target_constructor
+               assert target_constructor != null
+
+               if target_constructor.name != "init" and target_constructor.name != "new" then
+                       return target_constructor.mdoc
+               end
+
+               return mdoc
+       end
+end
+
 private class AutocompletePhase
        super Phase
 
@@ -113,8 +158,11 @@ private class AutocompletePhase
                var compile_dir = "NIT_VIM_DIR".environ
                if compile_dir.is_empty then compile_dir = "HOME".environ / ".vim/nit"
                compile_dir.mkdir
+
                var modules_stream = new OFStream.open(compile_dir / "modules.txt")
                var classes_stream = new OFStream.open(compile_dir / "classes.txt")
+               var constructors_stream = new OFStream.open(compile_dir / "constructors.txt")
+               var types_stream = new OFStream.open(compile_dir / "types.txt")
                var properties_stream = new OFStream.open(compile_dir / "properties.txt")
 
                # Got all known modules
@@ -124,13 +172,27 @@ private class AutocompletePhase
                end
 
                # TODO list other modules from the Nit lib
-               # TODO list submodules
 
                # Get all known classes
                for mclass in model.mclasses do
                        if not mainmodule.is_visible(mclass.intro_mmodule, public_visibility) then continue
+                       var mclass_intro = mclass.intro
+
+                       # Can it be instantiated?
+                       if mclass.kind != interface_kind and mclass.kind != abstract_kind then
+
+                               for prop in mclass.all_mproperties(mainmodule, public_visibility) do
+                                       if prop isa MMethod and prop.is_init then
+                                               mclass_intro.target_constructor = prop.intro
+                                               mclass_intro.write_to_stream constructors_stream
+                                       end
+                               end
+                               mclass_intro.target_constructor = null
+                       end
 
-                       mclass.intro.write_to_stream classes_stream
+                       # Always add to types and classes
+                       mclass.mclass_type.write_to_stream classes_stream
+                       mclass.mclass_type.write_to_stream types_stream
                end
 
                # Get all known properties
@@ -138,13 +200,24 @@ private class AutocompletePhase
                        var intro_mmodule = mproperty.intro_mclassdef.mmodule
                        if not mainmodule.is_visible(intro_mmodule, public_visibility) then continue
 
+                       # Is it a virtual type?
+                       if mproperty isa MVirtualTypeProp then
+                               mproperty.intro.write_to_stream types_stream
+                               continue
+                       end
+
+                       # Skip properties beginning with @ or _
+                       var first_letter = mproperty.name.chars.first
+                       if first_letter == '@' or first_letter == '_' then continue
+
                        mproperty.intro.write_to_stream properties_stream
                end
 
                # Close streams
-               for stream in [modules_stream, classes_stream, properties_stream] do
-                       stream.close
+               for stream in [modules_stream, classes_stream, properties_stream,
+                       types_stream, constructors_stream] do
 
+                       stream.close
                        var error = stream.last_error
                        if error != null then
                                toolcontext.error(null, "Failed to write Vim autocomplete file: {error}")