Skip to content

Latest commit

 

History

History
361 lines (244 loc) · 24.4 KB

SYNTAX-5-OTHER.markdown

File metadata and controls

361 lines (244 loc) · 24.4 KB

Advanced features

This document describes the more advanced features of Mindcode.

Compiler directives

Mindcode allows you to alter some compiler options in the source code using special #set commands. The basic syntax is:

#set option = value;

Some of these options can be alternatively specified as parameters of the command line compiler.

Supported compiler options are described below.

Option auto-printflush

Activates/deactivates automatic flushing of print output. Possible values are:

  • false: Mindcode doesn't flush print output automatically.
  • true (the default value): When the program contains at least one print or printchar instruction, and no printflush or draw print instructions, Mindcode adds a printflush message1 instruction at the end of the main program body, and generates a warning.

This feature is meant for small, test scripts, where a call to printflush() is easily missed. This situation requires new compilation and code injection into mlof processor when detected.

Option boundary-checks

This option activates/deactivates runtime checks and specifies which mechanism to use when runtime checks are active. Runtime checks are a debug feature and should be only active when developing/debugging your scripts, as they make the code larger and slower.

Currently, runtime checks are generated for these operations:

  • Accessing an element of an internal array by index: the check makes sure the index lies within bounds.

Possible values for the boundary-checks directive are:

  • none (the default value): no runtime checks are generated.
  • assert: runtime checks are generated using instructions provided by the MlogAssertions mod. The mod is available for Mindustry 7. If the mod is not installed, no runtime checks are performed, but otherwise the code runs as expected. Each runtime check takes 1 instruction. When the runtime check fails, the mod displays an error message over the processor for easier detection.
  • minimal: when the runtime check fails, the program execution stops on a jump instruction (this instruction permanently jumps to itself, which can be determined by inspecting the @counter variable in the Vars screen). Each runtime check takes 2 instructions.
  • simple: when the runtime check fails, the program execution stops on a stop instruction (again, this can be determined by inspecting the @counter variable). Each runtime check takes 3 instructions.
  • described: when the runtime check fails, the program execution stops on a stop instruction. However, a print instruction containing an error message is generated just before the stop instruction; after locating the faulting stop instruction, the error message can be read. Each runtime check takes 4 instructions.

Option goal

Use the goal option to specify whether Mindcode should prefer to generate smaller code, or faster code. Possible values are:

  • size: Mindcode tries to generate smaller code.
  • speed: Mindcode can generate additional instructions, if it makes the resulting code faster while adhering to the 1000 instructions limit. When several possible optimizations of this kind are available, the ones having the best effect (highest speedup per additional instruction generated) are selected until the instruction limit is reached.
  • auto (the default value): At this moment the setting is identical to speed.

Option instruction-limit

This option allows to change the instruction limit used by speed optimization. The speed optimization strives not to exceed this instruction limit. In some cases, the optimization cost estimates are too conservative - some optimizations applied together may lead to code reductions that are not known to individual optimizers considering each optimization in isolation. In these cases, increasing the instruction limit might allow more optimizations to be performed. When the resulting code exceeds 1000 instructions, it is not usable in Mindustry processors and the option should be decreased or set back to 1000. (A new feature, which would perform this trial-and-error optimization automatically, is planned.)

The limit only affects the optimization for speed. The option has no effect on code generated by the compiler or optimizers which do not work in the speed optimization mode, and doesn't help reduce the code size generated outside the optimization for speed mechanism.

It is also possible to decrease the instruction limit, if you wish so. The valid range for this compiler option is 1 to 100,000 for the command-line tool, and 1 to 1,500 for the web application.

Important

Setting the limit to a very high value can have severe impact on the performance of the compiler. High values of the instruction limit might cause the code compilation to take minutes or even hours to complete.

Option link-guards

Use the link-guards option to specify whether Mindcode should generate guard code for linked variables. Possible values are:

  • false: Mindcode doesn't generate guard code.
  • true (the default value): Mindcode generates guard code for all linked variables except.

For more details on guard code and rules for generating it, see Guard code for linked variables.

Option memory-model

This option has been added to support future enhancements of Mindcode. Setting the option doesn't have any effect at this moment.

Option optimization

Use the optimization option to set the optimization level of the compiler:

#set optimization = basic;

Possible values for this option are:

  • none: deactivates all optimizations.
  • basic: performs most optimizations, except those that depend on certain assumptions about the program or Mindustry Logic.
  • advanced: performs additional optimizations based upon some assumptions about Mindustry Logic (e.g. that numerical id produced by a lookup instruction for Mindustry content elements is stable) or the source code (e.g. that it doesn't depend on expressions converting null values to non-null ones).
  • experimental: perform optimizations that are currently in the experimental phase.

The default optimization level is advanced.

Option passes

Use the passes option to set the maximum number of optimization passes to be done:

#set passes = 10;

The default value is 3 for the web application and 25 for the command line tool. The number of optimization passes can be limited to a value between 1 and 1000 (inclusive).

A more complex code can usually benefit from more optimization passes. On the other hand, each optimization pass can take some time to complete. Limiting the total number can prevent optimization from taking too much time or consuming too many resources (this is a consideration for the web application).

Option remarks

This option controls the way remarks, generated through the remark() function, are propagated to the compiled code. Remarks are written into the compiled code as print instructions. Possible values of the remarks option are:

  • none: remarks are suppressed in the compiled code - they do not appear there at all.
  • passive: remarks are included in the compiled code, but a jump is generated in front each block of continuous remarks, so that the print statement themselves aren't executed. This is the default value.
  • active: remarks are included in the compiled code and are executed, producing actual output to the text buffer.

Passive remarks can be used for putting instructions or comments in the compiled code, or to mark a specific portion of the code. Remarks in a loop may help identifying individual iterations when the loop is unrolled, for example.

Active remarks can be used to easily add debugging output to a program that can be deactivated using a compiler option (potentially through a command line switch without modifying the source code).

Option sort-variables

The Vars screen of the Mindustry processor shows all variables and their values, but the variables are displayed in the order in which they were created. This typically results in a very chaotic order of variables, where variables defined by the user are mixed with temporary variables, making it quite difficult to find a specific variable in sufficiently large programs.

This option can be used to make variables be displayed in a Mindustry processor in a well-defined order. Mindcode compiler ensures that by prepending a special block at the beginning of the program which creates user-defined variables in a specific order without altering their value. (The packcolor instruction is used, which can read - and therefore create - up to four variables per instruction. The result is not stored anywhere so that the variable-ordering code block doesn't change values of any variables, and therefore the behavior of the program remains unaltered, except for possible difference in timing.)

The value assigned to the sort-variables directive is a list of variable categories:

It is possible to use the directive without specifying any value at all (#set sort-variables;). In this case, the categories will be processed in the order above.

When processing the directive, the categories are processed in the given order, with all variables in a category sorted alphabetically. This defines the resulting order of variables.

Note on the linked category: when a block is linked into the processor, a variable of that name is removed from the variable list. By putting the linked variables first, it is very easy to see which linked blocks used by the program are not linked under their proper names.

The number of variables being sorted is limited by the instruction limit. Should the resulting program size exceeds the instruction limit, some or all variables will remain unordered.

Option syntax

Chooses the syntax mode to be used for compilation. Possible values are:

  • relaxed (the default value): useful for shorter scripts, as it requires less boilerplate code.
  • strict: useful for larger projects, as it enforces additional rules designed to make source code more maintainable.
  • mixed: designed to help with a transition of relaxed syntax code to the strict standard. In this mode, code is compiled using the relaxed syntax rules, but all violations of the strict syntax rules are reported as warnings.

Option target

Use the target option to specify the Mindcode/Mindustry Logic version to be used by the compiler and processor emulator. The target versions consist of a major and minor version number. As of now, these versions exist:

Version Description
6.0 Mindustry Logic for the latest release of Mindustry 6.
7.0 Mindustry Logic for the latest release of Mindustry 7.
7.1 As above, with slightly revised syntax of some functions.
8.0 Mindustry Logic for the latest BE release of future Mindustry 8.

Version 8.0 will be the version corresponding to the official Mindustry 8 release. Future enhancements of Mindustry 8 Logic or, possibly, of the Mindcode language will be incorporated as separate versions with updated minor version number.

The target can be set using either just a major, or both major and minor version numbers. When specifying both numbers, the specified version is used. When specifying just the major version, the most recent minor version in given major category is used. Example:

#set target = 7;        // Sets version 7.1
#set target = 7.0;      // Sets version 7.0

To use a world-processor variant of Mindcode language, it is necessary to add W as a suffix to the version number:

#set target = 8W;     // World-processor logic version 8
#set target = 7.0W    // World-processor logic version 7.0

The same names of version targets is used with the -t / --target command-line option.

Individual optimization options

It is possible to set the level of individual optimization tasks. Every optimization is assigned a name, and this name can be used in the compiler directive like this:

#set expression-optimization = basic;

Most optimizations don't support the advanced level. For those the level advanced is the same as basic. The complete list of available optimizations, including the option name for setting the level of given optimization and availability of the advanced optimization level is:

Optimization Option name Advanced
Temporary Variables Elimination temp-variables-elimination N
Case Expression Optimization case-expression-optimization N
Dead Code Elimination dead-code-elimination N
Jump Normalization jump-normalization N
Jump Optimization jump-optimization N
Single Step Elimination single-step-elimination N
Expression Optimization expression-optimization Y
If Expression Optimization if-expression-optimization N
Data Flow Optimization data-flow-optimization N
Loop Hoisting loop-hoisting N
Loop Optimization loop-optimization N
Loop Unrolling loop-unrolling Y
Function Inlining function-inlining N
Case Switching case-switching N
Return Optimization case-switching N
Jump Straightening return-optimization N
Jump Threading jump-threading N
Unreachable Code Elimination unreachable-code-elimination N
Stack Optimization stack-optimization N
Print Merging print-merging Y

This table doesn't track which optimizations provide some functionality on the experimental level. This information is available in the individual optimization documentation.

You normally shouldn't need to deactivate any optimization, but if there was a bug in some of the optimizers, deactivating it might allow you to use Mindcode until a fix is available.

In particular, some optimizers expect to work on code that was already processed by different optimizations, so turning off some optimizations might render other optimizations ineffective. This is not a bug.

Creating custom mlog instructions

Mindcode provides a mechanism of encoding a custom instruction not known to Mindcode. Using custom instructions is useful in only a few cases:

  1. A new version of Mindustry (either an official release, or a bleeding edge version) creates new instructions not known to Mindcode.
  2. An instruction was not implemented correctly in Mindcode and a fix is not available.
  3. Mindustry got updated to allow new instructions created by mods, and there are mods with their own instructions.

Custom instructions may interact with Mindustry World or provide information about Mindustry World. If an instruction alters the program flow (for example, if a new function call instruction was added to Mindustry Logic), it cannot be safely encoded using this mechanism. In addition, some custom instructions might break existing optimizations through their side effects.

Custom instructions are created using the mlog() function:

  • The first argument to the mlog function needs to be a string literal. This literal is the instruction code.
  • All other arguments must be either literals, or user variables.
    • String literal: the text represented by the string literal is used as an instruction argument. If the in modifier is used, the string literal will be used as an argument including the enclosing double quotes.
    • Numeric literal: all other literals must not be marked with either modifier. The primary use for numeric literals is to provide fill-in values (typically zeroes) for unused instruction parameters.
    • User variable: the variable is used as an instruction argument. The argument must use the in and out modifier to inform Mindcode how the corresponding instruction argument behaves:
      • in: the argument represents an input value - the instruction reads and uses the value of the variable.
      • out: the argument represents an output value - the instruction produces a value and stores it in the variable.
      • in out: the argument represents an input/output value - the instruction both reads and uses the input value, and then updates the variable with a new value. With a possible exception to the sync instruction, no mlog instruction currently takes an input/output argument.

Mindcode assumes that a custom instruction interacts with the Mindustry World and cannot be safely removed from the program. This is not true for instructions which only return information about the Mindustry World, but do not modify or interact with it in any way. If you want to encode such an instruction, you can use the mlogSafe() function instead of mlog().

Tip

Although not strictly required, it is recommended to create an inline function with proper input/output parameters for each custom generated instruction. This way, the requirement that the mlog() function always uses user variables as arguments can be easily met, while allowing to use expressions for input parameters in the call to the enclosing function. See the examples below.

For better understanding, the creating of custom instructions will be demonstrated on existing instructions.

The format instruction

The format instruction was introduced in Mindustry Logic 8. When compiling for mindustry Logic 7, the instruction isn't available. We can create it using this code:

#set target = 7;
inline void format(value)
    mlog("format", in value);
end;

param a = 10;
println("The value is: {0}");
format(a * 20);

Compiling this code produces the following output:

set a 10
print "The value is: {0}\n"
op mul __tmp0 a 20
format __tmp0

Considerations:

  • The Print Merging optimization under Mindustry Logic 8 language target knows and properly handles the format instruction. When replaced by a custom instruction, the Print Merger won't be aware of it and might produce incorrect code. It would be necessary to turn off Print Merging optimization, if the format instruction was introduced in this way.
  • The processor emulator doesn't recognize custom instructions and won't handle them. The output produced by running the above code using the processor emulator would therefore be incorrect.

The draw print instruction

Mindustry 8 Logic adds new variants of the draw instruction, print being one of them. Under the Mindustry Logic 8 language target, this instruction is mapped to the drawPrint() function. Unfortunately, this instruction takes an additional keyword argument - alignment, which needs special treatment when defining a custom instruction. Each possible value of alignment needs to be handled separately.

#set target = 7;
inline void drawPrintCenter(x, y)
    mlog("draw", "print", in x, in y, "center", 0, 0, 0);
end;

inline void drawPrintBottomLeft(x, y)
    mlog("draw", "print", in x, in y, "bottomLeft", 0, 0, 0);
end;

drawPrintCenter(0, 10);
drawPrintBottomLeft(0, 20);

Result:

draw print 0 10 center 0 0 0
draw print 0 20 bottomLeft 0 0 0

Considerations:

  • The draw print instruction manipulates the text buffer and therefore interferes with the Print Merging optimization again. This optimization would need to be switched off.
  • A separate function for each possible alignment is required. Unused functions aren't compiled, so there isn't any cost in terms of instruction space to defining a function for each existing alignment, but it is tedious and cumbersome. Defining all possible variants for instructions that have several keyword arguments might become unfeasible.

The ucontrol getBlock instruction

The ucontrol getBlock instruction is an example of instruction which has output parameters. Also, we know it is an instruction which doesn't modify the Mindustry World and therefore is safe. Had it not be known by Mindcode, it could be defined like this:

// Using 'getBlock2' as a name to avoid clashing with the existing function name
inline def getBlock2(x, y, out type, out floor)
    mlogSafe("ucontrol", "getBlock", in x, in y, out type, out building, out floor);
    return building;
end;

x = floor(rand(100));
y = floor(rand(200));
// These two instruction generate the same mlog code:
building = getBlock(x, y, out type, out floor);
print(building, type, floor);

building = getBlock2(x, y, out type, out floor);
print(building, type, floor);

compiles to

op rand __tmp0 100 0
op floor x __tmp0 0
op rand __tmp2 200 0
op floor y __tmp2 0
ucontrol getBlock x y type building floor
print building
print type
print floor
ucontrol getBlock x y __fn0_type __fn0_building __fn0_floor
print __fn0_building
print __fn0_type
print __fn0_floor

The jump instruction

The jump instruction is a control flow instruction, an as such producing it through the mlog() function will break the compiled code. Anyhow, Mindcode will allow the following code to be compiled:

mlog("jump", 50, "always");

producing

jump 50 always

Considerations:

  • There aren't direct ways to obtain the targets for the jump instruction. Forcing the instruction to target the intended code might be very difficult (albeit not outright impossible when modifying the instruction to target labels instead).
  • Introducing jumps unrecognized by Mindcode would render most of the compiled code unsafe. Mindcode uses the knowledge of the program control flow to generate the code and make various optimizations. Introducing unrecognized control flow instructions would mean the compiled code and especially the optimizations are not correct.

« Previous: Functions   |   Up: Contents   |   Next: Code optimization »