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>
}
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
(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.
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
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
: 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
`--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
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")
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)
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)
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
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
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++;")
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};")
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)
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
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} \{")
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
# 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
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
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
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
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
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
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}")