diff --git a/lib/rouge/demos/ghc-cmm b/lib/rouge/demos/ghc-cmm new file mode 100644 index 0000000000..72b764e81d --- /dev/null +++ b/lib/rouge/demos/ghc-cmm @@ -0,0 +1,23 @@ +[lvl_s4t3_entry() // [R1] + { info_tbls: [(c4uB, + label: lvl_s4t3_info + rep: HeapRep 1 ptrs { Thunk } + srt: Nothing)] + stack_info: arg_space: 8 updfr_space: Just 8 + } + {offset + c4uB: // global + if ((Sp + -32) < SpLim) (likely: False) goto c4uC; else goto c4uD; + c4uC: // global + R1 = R1; + call (stg_gc_enter_1)(R1) args: 8, res: 0, upd: 8; + c4uD: // global + I64[Sp - 16] = stg_upd_frame_info; + P64[Sp - 8] = R1; + R2 = P64[R1 + 16]; + I64[Sp - 32] = stg_ap_p_info; + P64[Sp - 24] = Main.fib3_closure+1; + Sp = Sp - 32; + call GHC.Num.fromInteger_info(R2) args: 40, res: 0, upd: 24; + } + } diff --git a/lib/rouge/lexers/ghc_cmm.rb b/lib/rouge/lexers/ghc_cmm.rb new file mode 100644 index 0000000000..481bf20f93 --- /dev/null +++ b/lib/rouge/lexers/ghc_cmm.rb @@ -0,0 +1,340 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +# C minus minus (Cmm) is a pun on the name C++. It's an intermediate language +# of the Glasgow Haskell Compiler (GHC) that is very similar to C, but with +# many features missing and some special constructs. +# +# Cmm is a dialect of C--. The goal of this lexer is to use what GHC produces +# and parses (Cmm); C-- itself is not supported. +# +# https://gitlab.haskell.org/ghc/ghc/wikis/commentary/compiler/cmm-syntax +# +module Rouge + module Lexers + class GHCCmm < RegexLexer + title "GHC Cmm (C--)" + desc "GHC Cmm is the intermediate representation of the GHC Haskell compiler" + tag 'ghc-cmm' + filenames '*.cmm', '*.dump-cmm', '*.dump-cmm-*' + aliases 'cmm' + + ws = %r(\s|//.*?\n|/[*](?:[^*]|(?:[*][^/]))*[*]+/)mx + + # Make sure that this is not a preprocessor macro, e.g. `#if` or `#define`. + id = %r((?!#[a-zA-Z])[\w#\$%_']+) + + complex_id = %r( + (?:[\w#$%_']|\(\)|\(,\)|\[\]|[0-9])* + (?:[\w#$%_']+) + )mx + + state :root do + rule %r/\s+/m, Text + + # sections markers + rule %r/^=====.*=====$/, Generic::Heading + + # timestamps + rule %r/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ UTC$/, Comment::Single + + mixin :detect_section + mixin :preprocessor_macros + + mixin :info_tbls + mixin :comments + mixin :literals + mixin :keywords + mixin :types + mixin :infos + mixin :names + mixin :operators + + # escaped newline + rule %r/\\\n/, Text + + # rest is Text + rule %r/./, Text + end + + state :detect_section do + rule %r/(section)(\s+)/ do |m| + token Keyword, m[1] + token Text, m[2] + push :section + end + end + + state :section do + rule %r/"(data|cstring|text|rodata|relrodata|bss)"/, Name::Builtin + + rule %r/{/, Punctuation, :pop! + + mixin :names + mixin :operators + mixin :keywords + + rule %r/\s+/, Text + end + + state :preprocessor_macros do + rule %r/#(include|endif|else|if)/, Comment::Preproc + + rule %r{ + (\#define) + (#{ws}*) + (#{id}) + }mx do |m| + token Comment::Preproc, m[1] + recurse m[2] + token Name::Label, m[3] + end + end + + state :info_tbls do + rule %r/({ )(info_tbls)(:)/ do |m| + token Punctuation, m[1] + token Name::Entity, m[2] + token Punctuation, m[3] + + push :info_tbls_body + end + end + + state :info_tbls_body do + rule %r/}/, Punctuation, :pop! + rule %r/{/, Punctuation, :info_tbls_body + + rule %r/(?=label:)/ do + push :label + end + + rule %r{(\()(#{complex_id})(,)}mx do |m| + token Punctuation, m[1] + token Name::Label, m[2] + token Punctuation, m[3] + end + + mixin :literals + mixin :infos + mixin :keywords + mixin :operators + + rule %r/#{id}/, Text + rule %r/\s+/, Text + end + + state :label do + mixin :infos + mixin :names + mixin :keywords + mixin :operators + + rule %r/[^\S\n]+/, Text # Tab, space, etc. but not newline! + rule %r/\n/, Text, :pop! + end + + state :comments do + rule %r/\/{2}.*/, Comment::Single + rule %r/\(likely.*?\)/, Comment + rule %r/\/\*.*?\*\//m, Comment::Multiline + end + + state :literals do + rule %r/-?[0-9]+\.[0-9]+/, Literal::Number::Float + rule %r/-?[0-9]+/, Literal::Number::Integer + rule %r/"/, Literal::String::Delimiter, :literal_string + end + + state :literal_string do + # quotes + rule %r/\\./, Literal::String::Escape + rule %r/%./, Literal::String::Symbol + rule %r/"/, Literal::String::Delimiter, :pop! + rule %r/./, Literal::String + end + + state :operators do + rule %r/\.\./, Operator + rule %r/[+\-*\/<>=!&|~]/, Operator + rule %r/[\[\].{}:;,()]/, Punctuation + end + + state :keywords do + rule %r/(const)(\s+)/ do |m| + token Keyword::Constant, m[1] + token Text, m[2] + end + + rule %r/"/, Literal::String::Double + + rule %r/(switch)([^{]*)({)/ do |m| + token Keyword, m[1] + recurse m[2] + token Punctuation, m[3] + end + + rule %r/(arg|result)(#{ws}+)(hints)(:)/ do |m| + token Name::Property, m[1] + recurse m[2] + token Name::Property, m[3] + token Punctuation, m[4] + end + + rule %r/(returns)(#{ws}*)(to)/ do |m| + token Keyword, m[1] + recurse m[2] + token Keyword, m[3] + end + + rule %r/(never)(#{ws}*)(returns)/ do |m| + token Keyword, m[1] + recurse m[2] + token Keyword, m[3] + end + + rule %r{(return)(#{ws}*)(\()} do |m| + token Keyword, m[1] + recurse m[2] + token Punctuation, m[3] + end + + rule %r{(if|else|goto|call|offset|import|jump|ccall|foreign|prim|case|unwind|export|reserve|push)(#{ws})} do |m| + token Keyword, m[1] + recurse m[2] + end + + rule %r{(default)(#{ws}*)(:)} do |m| + token Keyword, m[1] + recurse m[2] + token Punctuation, m[3] + end + end + + state :types do + # Memory access: `type[42]` + # Note: Only a token for type is produced. + rule %r/(#{id})(?=\[[^\]])/ do |m| + token Keyword::Type, m[1] + end + + # Array type: `type[]` + rule %r/(#{id}\[\])/ do |m| + token Keyword::Type, m[1] + end + + # Capture macro substitutions before lexing typed declarations + # I.e. there is no type in `PREPROCESSOR_MACRO_VARIABLE someFun()` + rule %r{ + (^#{id}) + (#{ws}+) + (#{id}) + (#{ws}*) + (\() + }mx do |m| + token Name::Label, m[1] + recurse m[2] + token Name::Function, m[3] + recurse m[4] + token Punctuation, m[5] + end + + # Type in variable or parameter declaration: + # `type /* optional whitespace */ var_name /* optional whitespace */;` + # `type /* optional whitespace */ var_name /* optional whitespace */, var_name2` + # `(type /* optional whitespace */ var_name /* optional whitespace */)` + # Note: Only the token for type is produced here. + rule %r{ + (^#{id}) + (#{ws}+) + (#{id}) + }mx do |m| + token Keyword::Type, m[1] + recurse m[2] + token Name::Label, m[3] + end + end + + state :infos do + rule %r/(args|res|upd|label|rep|srt|arity|fun_type|arg_space|updfr_space)(:)/ do |m| + token Name::Property, m[1] + token Punctuation, m[2] + end + + rule %r/(stack_info)(:)/ do |m| + token Name::Entity, m[1] + token Punctuation, m[2] + end + end + + state :names do + rule %r/(::)(#{ws}*)([A-Z]\w+)/ do |m| + token Operator, m[1] + recurse m[2] + token Keyword::Type, m[3] + end + + rule %r/<(#{id})>/, Name::Builtin + + rule %r/(Sp|SpLim|Hp|HpLim|HpAlloc|BaseReg|CurrentNursery|CurrentTSO|R\d{1,2}|gcptr)(?!#{id})/, Name::Variable::Global + rule %r/([A-Z]#{id})(\.)/ do |m| + token Name::Namespace, m[1] + token Punctuation, m[2] + push :namespace_name + end + + # Inline function calls: + # ``` + # arg1 `lt` arg2 + # ``` + rule %r/(`)(#{id})(`)/ do |m| + token Punctuation, m[1] + token Name::Function, m[2] + token Punctuation, m[3] + end + + # Function: `name /* optional whitespace */ (` + # Function (arguments via explicit stack handling): `name /* optional whitespace */ {` + rule %r{(?= + #{complex_id} + #{ws}* + [\{\(] + )}mx do + push :function + end + + rule %r/CLOSURE/, Keyword::Type + rule %r/#{complex_id}/, Name::Label + end + + state :namespace_name do + rule %r/([A-Z]#{id})(\.)/ do |m| + token Name::Namespace, m[1] + token Punctuation, m[2] + end + + rule %r{(#{complex_id})(#{ws}*)([\{\(])}mx do |m| + token Name::Function, m[1] + recurse m[2] + token Punctuation, m[3] + pop! + end + + rule %r/#{complex_id}/, Name::Label, :pop! + + rule %r/(?=.)/m do + pop! + end + end + + state :function do + rule %r/INFO_TABLE_FUN|INFO_TABLE_CONSTR|INFO_TABLE_SELECTOR|INFO_TABLE_RET|INFO_TABLE/, Name::Builtin + rule %r/%#{id}/, Name::Builtin + rule %r/#{complex_id}/, Name::Function + rule %r/\s+/, Text + rule %r/[({]/, Punctuation, :pop! + mixin :comments + end + end + end +end diff --git a/spec/lexers/ghc_cmm_spec.rb b/spec/lexers/ghc_cmm_spec.rb new file mode 100644 index 0000000000..33676d6e8e --- /dev/null +++ b/spec/lexers/ghc_cmm_spec.rb @@ -0,0 +1,1129 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +describe Rouge::Lexers::GHCCmm do + let(:subject) { Rouge::Lexers::GHCCmm.new } + + describe 'guessing' do + include Support::Guessing + + it 'guesses by filename' do + assert_guess :filename => 'Main.cmm' + assert_guess :filename => 'Main.dump-cmm' + assert_guess :filename => 'Main.dump-cmm-switch' + assert_guess :filename => 'Main.dump-cmm-sp' + assert_guess :filename => 'Main.dump-cmm-sink' + assert_guess :filename => 'Main.dump-cmm-raw' + assert_guess :filename => 'Main.dump-cmm-info' + assert_guess :filename => 'Main.dump-cmm-from-stg' + assert_guess :filename => 'Main.dump-cmm-cps' + assert_guess :filename => 'Main.dump-cmm-cfg' + assert_guess :filename => 'Main.dump-cmm-cbe' + assert_guess :filename => 'Main.dump-cmm-caf' + end + end + + describe 'lexing' do + include Support::Lexing + + it 'should lex section markers as headings' do + core = '==================== Output Cmm ====================' + assert_tokens_equal core, ['Generic.Heading', core] + end + + it 'should lex timestamps as comments' do + core = '2019-12-24 13:23:29.666399 UTC' + assert_tokens_equal core, ['Comment.Single', core] + end + + it 'should lex brackets as punctuation' do + core = '[]' + assert_tokens_equal core, ['Punctuation', '[]'] + end + + it 'should lex a simple section' do + core = '[section ""data" . Main.fib1_closure" { + Main.fib1_closure: + const GHC.Integer.Type.S#_con_info; + const 0; + }]' + + assert_tokens_equal core, + ['Punctuation', '['], + ['Keyword', 'section'], + ['Text', ' '], + ['Literal.String.Double', '"'], + ['Name.Builtin', '"data"'], + ['Text', ' '], + ['Punctuation', '.'], + ['Text', ' '], + ['Name.Namespace', 'Main'], ['Punctuation', '.'], ['Name.Label', 'fib1_closure'], + ['Literal.String.Double', '"'], + ['Text', ' '], + ['Punctuation', '{'], + ['Text', "\n "], + ['Name.Namespace', 'Main'], ['Punctuation', '.'], ['Name.Label', 'fib1_closure'], + ['Punctuation', ':'], + ['Text', "\n "], + ['Keyword.Constant', 'const'], + ['Text', ' '], + ['Name.Namespace', 'GHC'], ['Punctuation', '.'], ['Name.Namespace', 'Integer'], ['Punctuation', '.'], ['Name.Namespace', 'Type'], ['Punctuation', '.'], ['Name.Label', 'S#_con_info'], + ['Punctuation', ';'], + ['Text', "\n "], + ['Keyword.Constant', 'const'], + ['Text', ' '], + ['Literal.Number.Integer', '0'], + ['Punctuation', ';'], + ['Text', "\n "], + ['Punctuation', '}]'] + end + + it 'should lex sections with function definitions' do + core = '[section ""data" . u4uh_srt" { + u4uh_srt: + const stg_SRT_1_info; + const GHC.Integer.Type.plusInteger_closure; + const 0; + }, + Main.fib_fib_entry() // [R2]' + + assert_tokens_equal core, + ['Punctuation', '['], + ['Keyword', 'section'], + ['Text', ' '], + ['Literal.String.Double', '"'], + ['Name.Builtin', '"data"'], + ['Text', ' '], + ['Punctuation', '.'], + ['Text', ' '], + ['Name.Label', 'u4uh_srt'], + ['Literal.String.Double', '"'], + ['Text', ' '], + ['Punctuation', '{'], + + ['Text', "\n "], + ['Name.Label', 'u4uh_srt'], + ['Punctuation', ':'], + ['Text', "\n "], + ['Keyword.Constant', 'const'], + ['Text', ' '], + ['Name.Label', 'stg_SRT_1_info'], + ['Punctuation', ';'], + + ['Text', "\n "], + ['Keyword.Constant', 'const'], + ['Text', ' '], + ['Name.Namespace', 'GHC'], ['Punctuation', '.'], ['Name.Namespace', 'Integer'], ['Punctuation', '.'], ['Name.Namespace', 'Type'], ['Punctuation', '.'], ['Name.Label', 'plusInteger_closure'], + ['Punctuation', ';'], + + ['Text', "\n "], + ['Keyword.Constant', 'const'], + ['Text', ' '], + ['Literal.Number.Integer', '0'], + ['Punctuation', ';'], + + ['Text', "\n "], + ['Punctuation', '},'], + + ['Text', "\n "], + ['Name.Namespace', 'Main'], ['Punctuation', '.'], ['Name.Function', 'fib_fib_entry'], + ['Punctuation', '()'], + ['Text', ' '], + ['Comment.Single', '// [R2]'] + end + + it 'should lex cstring sections' do + core = '[section ""cstring" . Main.$trModule2_bytes" { + Main.$trModule2_bytes: + I8[] [77,97,105,110] + }]' + assert_tokens_equal core, + ['Punctuation', '['], + ['Keyword', 'section'], + ['Text', ' '], + ['Literal.String.Double', '"'], + ['Name.Builtin', '"cstring"'], + ['Text', ' '], + ['Punctuation', '.'], + ['Text', ' '], + ['Name.Namespace', 'Main'], ['Punctuation', '.'], ['Name.Label', '$trModule2_bytes'], + ['Literal.String.Double', '"'], + ['Text', ' '], + ['Punctuation', '{'], + ['Text', "\n "], + ['Name.Namespace', 'Main'], ['Punctuation', '.'], ['Name.Label', '$trModule2_bytes'], + ['Punctuation', ':'], + ['Text', "\n "], + ['Keyword.Type', 'I8[]'], + ['Text', ' '], + ['Punctuation', '['], + ['Literal.Number.Integer', '77'], + ['Punctuation', ','], + ['Literal.Number.Integer', '97'], + ['Punctuation', ','], + ['Literal.Number.Integer', '105'], + ['Punctuation', ','], + ['Literal.Number.Integer', '110'], + ['Punctuation', ']'], + ['Text', "\n "], + ['Punctuation', '}]'] + end + + it 'should lex handwritten sections' do + core = 'section "data" { + no_break_on_exception: W_[1]; +}' + + assert_tokens_equal core, + ['Keyword', 'section'], + ['Text', ' '], + ['Name.Builtin', '"data"'], + ['Text', ' '], + ['Punctuation', '{'], + ['Text', "\n "], + ['Name.Label', 'no_break_on_exception'], + ['Punctuation', ':'], + ['Text', ' '], + ['Keyword.Type', 'W_'], + ['Punctuation', '['], + ['Literal.Number.Integer', '1'], + ['Punctuation', '];'], + ['Text', "\n"], + ['Punctuation', '}'] + end + + it 'should lex operators and comparisons' do + core = 'if ((Sp + -16) < SpLim) (likely: False) goto c4tE; else goto c4tF;' + + assert_tokens_equal core, + ['Keyword', 'if'], + ['Text', ' '], + ['Punctuation', '(('], + ['Name.Variable.Global', 'Sp'], + ['Text', ' '], + ['Operator', '+'], + ['Text', ' '], + ['Literal.Number.Integer', '-16'], + ['Punctuation', ')'], + ['Text', ' '], + ['Operator', '<'], + ['Text', ' '], + ['Name.Variable.Global', 'SpLim'], + ['Punctuation', ')'], + ['Text', ' '], + ['Comment', '(likely: False)'], + ['Text', ' '], + ['Keyword', 'goto'], + ['Text', ' '], + ['Name.Label', 'c4tE'], + ['Punctuation', ';'], + ['Text', ' '], + ['Keyword', 'else'], + ['Text', ' '], + ['Keyword', 'goto'], + ['Text', ' '], + ['Name.Label', 'c4tF'], + ['Punctuation', ';'] + end + + it 'should lex Hp and HpLim as global variables' do + core = 'if (Hp > HpLim) (likely: False) goto c4vy; else goto c4vx;' + + assert_tokens_equal core, + ['Keyword', 'if'], + ['Text', ' '], + ['Punctuation', '('], + ['Name.Variable.Global', 'Hp'], + ['Text', ' '], + ['Operator', '>'], + ['Text', ' '], + ['Name.Variable.Global', 'HpLim'], + ['Punctuation', ')'], + ['Text', ' '], + ['Comment', '(likely: False)'], + ['Text', ' '], + ['Keyword', 'goto'], + ['Text', ' '], + ['Name.Label', 'c4vy'], + ['Punctuation', ';'], + ['Text', ' '], + ['Keyword', 'else'], + ['Text', ' '], + ['Keyword', 'goto'], + ['Text', ' '], + ['Name.Label', 'c4vx'], + ['Punctuation', ';'] + end + + it 'should lex registers as global variables' do + core = 'R2 = R2;' + + assert_tokens_equal core, + ['Name.Variable.Global', 'R2'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Name.Variable.Global', 'R2'], + ['Punctuation', ';'] + end + + it 'should lex calls' do + core = 'call GHC.Integer.Type.eqInteger#_info(R3, + R2) returns to c4ty, args: 8, res: 8, upd: 8;' + + assert_tokens_equal core, + ['Keyword', 'call'], + ['Text', ' '], + ['Name.Namespace', 'GHC'], ['Punctuation', '.'], ['Name.Namespace', 'Integer'], ['Punctuation', '.'], ['Name.Namespace', 'Type'], ['Punctuation', '.'], ['Name.Function', 'eqInteger#_info'], + ['Punctuation', '('], + ['Name.Variable.Global', 'R3'], + ['Punctuation', ','], + ['Text', "\n "], + ['Name.Variable.Global', 'R2'], + ['Punctuation', ')'], + ['Text', ' '], + ['Keyword', 'returns'], + ['Text', ' '], + ['Keyword', 'to'], + ['Text', ' '], + ['Name.Label', 'c4ty'], + ['Punctuation', ','], + ['Text', ' '], + ['Name.Property', 'args'], + ['Punctuation', ':'], + ['Text', ' '], + ['Literal.Number.Integer', '8'], + ['Punctuation', ','], + ['Text', ' '], + ['Name.Property', 'res'], + ['Punctuation', ':'], + ['Text', ' '], + ['Literal.Number.Integer', '8'], + ['Punctuation', ','], + ['Text', ' '], + ['Name.Property', 'upd'], + ['Punctuation', ':'], + ['Text', ' '], + ['Literal.Number.Integer', '8'], + ['Punctuation', ';'] + end + + it 'should lex offset' do + core = 'offset +' + + assert_tokens_equal core, ['Keyword', 'offset'], ['Text', "\n"] + end + + it 'should lex array accesses' do + core = 'I64[Sp] = c4tZ;' + + assert_tokens_equal core, + ['Keyword.Type', 'I64'], + ['Punctuation', '['], + ['Name.Variable.Global', 'Sp'], + ['Punctuation', ']'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Name.Label', 'c4tZ'], + ['Punctuation', ';'] + + end + + it 'should lex multi-line comments' do + core = '/* for objects that are *less* than the size of a word, make sure we + * round up to the nearest word for the size of the array. + */' + + assert_tokens_equal core, + ['Comment.Multiline', core] + end + + it 'should lex function calls that start with a register name "prefix"' do + core = 'Sp_adj(-1);' + + assert_tokens_equal core, + ['Name.Function', 'Sp_adj'], + ['Punctuation', '('], + ['Literal.Number.Integer', '-1'], + ['Punctuation', ');'] + end + + it 'should lex #include' do + core = '#include "Cmm.h"' + + assert_tokens_equal core, + ['Comment.Preproc', '#include'], + ['Text', ' '], + ['Literal.String.Delimiter', '"'], + ['Literal.String', 'Cmm.h'], + ['Literal.String.Delimiter', '"'] + end + + it 'should lex #if' do + core = '#if defined(__PIC__) +import pthread_mutex_lock; +import pthread_mutex_unlock; +#endif' + + assert_tokens_equal core, + ['Comment.Preproc', '#if'], + ['Text', ' '], + ['Name.Function', 'defined'], + ['Punctuation', '('], + ['Name.Label', '__PIC__'], + ['Punctuation', ')'], + ['Text', "\n"], + ['Keyword', 'import'], + ['Text', ' '], + ['Name.Label', 'pthread_mutex_lock'], + ['Punctuation', ';'], + ['Text', "\n"], + ['Keyword', 'import'], + ['Text', ' '], + ['Name.Label', 'pthread_mutex_unlock'], + ['Punctuation', ';'], + ['Text', "\n"], + ['Comment.Preproc', '#endif'] + end + + it 'should lex #else' do + core = '#else' + + assert_tokens_equal core, + ['Comment.Preproc', '#else'] + end + + it 'should lex a simple #define statement' do + core = '#define BA_ALIGN 16' + + assert_tokens_equal core, + ['Comment.Preproc', '#define'], + ['Text', ' '], + ['Name.Label', 'BA_ALIGN'], + ['Text', ' '], + ['Literal.Number.Integer', '16'] + end + + it 'should lex a simple #define statement with comment' do + core = '#define /* comment */ BA_ALIGN /* comment */ 16' + + assert_tokens_equal core, + ['Comment.Preproc', '#define'], + ['Text', ' '], + ['Comment.Multiline', '/* comment */'], + ['Text', ' '], + ['Name.Label', 'BA_ALIGN'], + ['Text', ' '], + ['Comment.Multiline', '/* comment */'], + ['Text', ' '], + ['Literal.Number.Integer', '16'] + end + + it 'should lex a #define statement with an expression' do + core = '#define BA_MASK (BA_ALIGN-1)' + + assert_tokens_equal core, + ['Comment.Preproc', '#define'], + ['Text', ' '], + ['Name.Label', 'BA_MASK'], + ['Text', ' '], + ['Punctuation', '('], + ['Name.Label', 'BA_ALIGN'], + ['Literal.Number.Integer', '-1'], + ['Punctuation', ')'] + end + + it 'should lex functions with comments' do + core = '/* comment */ stg_isEmptyMVarzh /* comment */ ( /* comment */ P_ mvar /* comment */ ) // single line comment +{' + assert_tokens_equal core, + ['Comment.Multiline', '/* comment */'], + ['Text', ' '], + ['Name.Function', 'stg_isEmptyMVarzh'], + ['Text', ' '], + ['Comment.Multiline', '/* comment */'], + ['Text', ' '], + ['Punctuation', '('], + ['Text', ' '], + ['Comment.Multiline', '/* comment */'], + ['Text', ' '], + ['Keyword.Type', 'P_'], + ['Text', ' '], + ['Name.Label', 'mvar'], + ['Text', ' '], + ['Comment.Multiline', '/* comment */'], + ['Text', ' '], + ['Punctuation', ')'], + ['Text', ' '], + ['Comment.Single', '// single line comment'], + ['Text', "\n"], + ['Punctuation', '{'] + end + + it 'should lex functions and return statements' do + core = 'stg_isEmptyMVarzh ( P_ mvar /* :: MVar a */ ) +{ + if (StgMVar_value(mvar) == stg_END_TSO_QUEUE_closure) { + return (1); + } else { + return (0); + } +}' + assert_tokens_equal core, + ['Name.Function', 'stg_isEmptyMVarzh'], + ['Text', ' '], + ['Punctuation', '('], + ['Text', ' '], + ['Keyword.Type', 'P_'], + ['Text', ' '], + ['Name.Label', 'mvar'], + ['Text', ' '], + ['Comment.Multiline', '/* :: MVar a */'], + ['Text', ' '], + ['Punctuation', ')'], + ['Text', "\n"], + ['Punctuation', '{'], + ['Text', "\n "], + ['Keyword', 'if'], + ['Text', ' '], + ['Punctuation', '('], + ['Name.Function', 'StgMVar_value'], + ['Punctuation', '('], + ['Name.Label', 'mvar'], + ['Punctuation', ')'], + ['Text', ' '], + ['Operator', '=='], + ['Text', ' '], + ['Name.Label', 'stg_END_TSO_QUEUE_closure'], + ['Punctuation', ')'], + ['Text', ' '], + ['Punctuation', '{'], + ['Text', "\n "], + ['Keyword', 'return'], + ['Text', ' '], + ['Punctuation', '('], + ['Literal.Number.Integer', '1'], + ['Punctuation', ');'], + ['Text', "\n "], + ['Punctuation', '}'], + ['Text', ' '], + ['Keyword', 'else'], + ['Text', ' '], + ['Punctuation', '{'], + ['Text', "\n "], + ['Keyword', 'return'], + ['Text', ' '], + ['Punctuation', '('], + ['Literal.Number.Integer', '0'], + ['Punctuation', ');'], + ['Text', "\n "], + ['Punctuation', '}'], + ['Text', "\n"], + ['Punctuation', '}'] + end + + it 'should lex ccall' do + core = 'ccall runCFinalizers(list);' + + assert_tokens_equal core, + ['Keyword', 'ccall'], + ['Text', ' '], + ['Name.Function', 'runCFinalizers'], + ['Punctuation', '('], + ['Name.Label', 'list'], + ['Punctuation', ');'] + end + + it 'should lex jump' do + core = 'jump stg_yield_noregs();' + + assert_tokens_equal core, + ['Keyword', 'jump'], + ['Text', ' '], + ['Name.Function', 'stg_yield_noregs'], + ['Punctuation', '();'] + end + + it 'should lex foreign calls' do + core = '(len) = foreign "C" heap_view_closureSize(UNTAG(clos) "ptr");' + + assert_tokens_equal core, + ['Punctuation', '('], + ['Name.Label', 'len'], + ['Punctuation', ')'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Keyword', 'foreign'], + ['Text', ' '], + ['Literal.String.Delimiter', '"'], + ['Literal.String', 'C'], + ['Literal.String.Delimiter', '"'], + ['Text', ' '], + ['Name.Function', 'heap_view_closureSize'], + ['Punctuation', '('], + ['Name.Function', 'UNTAG'], + ['Punctuation', '('], + ['Name.Label', 'clos'], + ['Punctuation', ')'], + ['Text', ' '], + ['Literal.String.Delimiter', '"'], + ['Literal.String', 'ptr'], + ['Literal.String.Delimiter', '"'], + ['Punctuation', ');'] + end + + it 'should lex prim calls' do + core = 'prim %memcpy(BYTE_ARR_CTS(new_mba), BYTE_ARR_CTS(mba), + StgArrBytes_bytes(mba), SIZEOF_W);' + + assert_tokens_equal core, + ['Keyword', 'prim'], + ['Text', ' '], + ['Name.Builtin', '%memcpy'], + ['Punctuation', '('], + ['Name.Function', 'BYTE_ARR_CTS'], + ['Punctuation', '('], + ['Name.Label', 'new_mba'], + ['Punctuation', '),'], + ['Text', ' '], + ['Name.Function', 'BYTE_ARR_CTS'], + ['Punctuation', '('], + ['Name.Label', 'mba'], + ['Punctuation', '),'], + ['Text', "\n "], + ['Name.Function', 'StgArrBytes_bytes'], + ['Punctuation', '('], + ['Name.Label', 'mba'], + ['Punctuation', '),'], + ['Text', ' '], + ['Name.Label', 'SIZEOF_W'], + ['Punctuation', ');'] + end + + it 'should lex switch statements and .. operators' do + core = 'switch [INVALID_OBJECT .. N_CLOSURE_TYPES] + (TO_W_( %INFO_TYPE(%STD_INFO(info)) )) { + case + IND, + IND_STATIC: + { + fun = StgInd_indirectee(fun); + goto again; + } + default: + { + jump %ENTRY_CODE(info) (UNTAG(fun)); + } + }' + + assert_tokens_equal core, + ['Keyword', 'switch'], + ['Text', ' '], + ['Punctuation', '['], + ['Name.Label', 'INVALID_OBJECT'], + ['Text', ' '], + ['Operator', '..'], + ['Text', ' '], + ['Name.Label', 'N_CLOSURE_TYPES'], + ['Punctuation', ']'], + ['Text', "\n "], + ['Punctuation', '('], + ['Name.Function', 'TO_W_'], + ['Punctuation', '('], + ['Text', ' '], + ['Name.Builtin', '%INFO_TYPE'], + ['Punctuation', '('], + ['Name.Builtin', '%STD_INFO'], + ['Punctuation', '('], + ['Name.Label', 'info'], + ['Punctuation', '))'], + ['Text', ' '], + ['Punctuation', '))'], + ['Text', ' '], + ['Punctuation', '{'], + ['Text', "\n "], + ['Keyword', 'case'], + ['Text', "\n "], + ['Name.Label', 'IND'], + ['Punctuation', ','], + ['Text', "\n "], + ['Name.Label', 'IND_STATIC'], + ['Punctuation', ':'], + ['Text', "\n "], + ['Punctuation', '{'], + ['Text', "\n "], + ['Name.Label', 'fun'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Name.Function', 'StgInd_indirectee'], + ['Punctuation', '('], + ['Name.Label', 'fun'], + ['Punctuation', ');'], + ['Text', "\n "], + ['Keyword', 'goto'], + ['Text', ' '], + ['Name.Label', 'again'], + ['Punctuation', ';'], + ['Text', "\n "], + ['Punctuation', '}'], + ['Text', "\n "], + ['Keyword', 'default'], + ['Punctuation', ':'], + ['Text', "\n "], + ['Punctuation', '{'], + ['Text', "\n "], + ['Keyword', 'jump'], + ['Text', ' '], + ['Name.Builtin', '%ENTRY_CODE'], + ['Punctuation', '('], + ['Name.Label', 'info'], + ['Punctuation', ')'], + ['Text', ' '], + ['Punctuation', '('], + ['Name.Function', 'UNTAG'], + ['Punctuation', '('], + ['Name.Label', 'fun'], + ['Punctuation', '));'], + ['Text', "\n "], + ['Punctuation', '}'], + ['Text', "\n "], + ['Punctuation', '}'] + end + + it 'should lex switch statements with two expressions' do + core = 'switch [0 .. N_CLOSURE_TYPES] type {' + + assert_tokens_equal core, + ['Keyword', 'switch'], + ['Text', ' '], + ['Punctuation', '['], + ['Literal.Number.Integer', '0'], + ['Text', ' '], + ['Operator', '..'], + ['Text', ' '], + ['Name.Label', 'N_CLOSURE_TYPES'], + ['Punctuation', ']'], + ['Text', ' '], + ['Name.Label', 'type'], + ['Text', ' '], + ['Punctuation', '{'] + end + + it 'should lex a "never returns" ccall' do + core = 'ccall barf("PAP object (%p) entered!", R1) never returns;' + + assert_tokens_equal core, + ['Keyword', 'ccall'], + ['Text', ' '], + ['Name.Function', 'barf'], + ['Punctuation', '('], + ['Literal.String.Delimiter', '"'], + ['Literal.String', 'PAP object ('], + ['Literal.String.Symbol', '%p'], + ['Literal.String', ') entered!'], + ['Literal.String.Delimiter', '"'], + ['Punctuation', ','], + ['Text', ' '], + ['Name.Variable.Global', 'R1'], + ['Punctuation', ')'], + ['Text', ' '], + ['Keyword', 'never'], + ['Text', ' '], + ['Keyword', 'returns'], + ['Punctuation', ';'] + end + + it 'should lex type annotations' do + core = '0::CBool' + + assert_tokens_equal core, + ['Literal.Number.Integer', '0'], + ['Operator', '::'], + ['Keyword.Type', 'CBool'] + end + + it 'should lex unwind' do + core = 'unwind Sp = Sp + WDS(1);' + + assert_tokens_equal core, + ['Keyword', 'unwind'], + ['Text', ' '], + ['Name.Variable.Global', 'Sp'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Name.Variable.Global', 'Sp'], + ['Text', ' '], + ['Operator', '+'], + ['Text', ' '], + ['Name.Function', 'WDS'], + ['Punctuation', '('], + ['Literal.Number.Integer', '1'], + ['Punctuation', ');'] + end + + it 'should lex functions with explicit stack handling' do + core = 'stg_maskAsyncExceptionszh /* explicit stack */ +{' + + assert_tokens_equal core, + ['Name.Function', 'stg_maskAsyncExceptionszh'], + ['Text', ' '], + ['Comment.Multiline', '/* explicit stack */'], + ['Text', "\n"], + ['Punctuation', '{'] + end + + it 'should lex functions with explicit stack handling and multiline comment' do + core = 'stg_raisezh +/* + * comment + */ +{' + + assert_tokens_equal core, + ['Name.Function', 'stg_raisezh'], + ['Text', "\n"], + ['Comment.Multiline', "/*\n * comment\n */"], + ['Text', "\n"], + ['Punctuation', '{'] + end + + it 'should lex functions that are prefixed with % as builtin' do + core = 'jump %GET_ENTRY(UNTAG(R1)) [R1];' + + assert_tokens_equal core, + ['Keyword', 'jump'], + ['Text', ' '], + ['Name.Builtin', '%GET_ENTRY'], + ['Punctuation', '('], + ['Name.Function', 'UNTAG'], + ['Punctuation', '('], + ['Name.Variable.Global', 'R1'], + ['Punctuation', '))'], + ['Text', ' '], + ['Punctuation', '['], + ['Name.Variable.Global', 'R1'], + ['Punctuation', '];'] + end + + it 'should lex typed memory accesses' do + core = 'Sp(i) = W_[p];' + + assert_tokens_equal core, + ['Name.Variable.Global', 'Sp'], + ['Punctuation', '('], + ['Name.Label', 'i'], + ['Punctuation', ')'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Keyword.Type', 'W_'], + ['Punctuation', '['], + ['Name.Label', 'p'], + ['Punctuation', '];'] + end + + it 'should lex return' do + core = 'return(h);' + + assert_tokens_equal core, + ['Keyword', 'return'], + ['Punctuation', '('], + ['Name.Label', 'h'], + ['Punctuation', ');'] + + core = 'return ();' + + assert_tokens_equal core, + ['Keyword', 'return'], + ['Text', ' '], + ['Punctuation', '();'] + end + + it 'should lex literal floating point numbers' do + core = 'const 1.5 :: W64;' + + assert_tokens_equal core, + ['Keyword.Constant', 'const'], + ['Text', ' '], + ['Literal.Number.Float', '1.5'], + ['Text', ' '], + ['Operator', '::'], + ['Text', ' '], + ['Keyword.Type', 'W64'], + ['Punctuation', ';'] + end + + it 'should lex names in statements correctly' do + core = 'R4 = GHC.Types.True_closure+2;' + + assert_tokens_equal core, + ['Name.Variable.Global', 'R4'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Name.Namespace', 'GHC'], + ['Punctuation', '.'], + ['Name.Namespace', 'Types'], + ['Punctuation', '.'], + ['Name.Label', 'True_closure'], + ['Operator', '+'], + ['Literal.Number.Integer', '2'], + ['Punctuation', ';'] + end + + it 'should lex info_tbls' do + core = + '{ info_tbls: [(c3zg, + label: Main.string_info + rep: HeapRep static { Thunk } + srt: Nothing)] + stack_info: arg_space: 8 updfr_space: Just 8 +}' + assert_tokens_equal core, + ['Punctuation', '{ '], + ['Name.Entity', 'info_tbls'], + ['Punctuation', ':'], + ['Text', ' '], + ['Punctuation', '[('], + ['Name.Label', 'c3zg'], + ['Punctuation', ','], + ['Text', "\n "], + ['Name.Property', 'label'], + ['Punctuation', ':'], + ['Text', ' '], + ['Name.Namespace', 'Main'], + ['Punctuation', '.'], + ['Name.Label', 'string_info'], + ['Text', "\n "], + ['Name.Property', 'rep'], + ['Punctuation', ':'], + ['Text', ' HeapRep static '], + ['Punctuation', '{'], + ['Text', ' Thunk '], + ['Punctuation', '}'], + ['Text', "\n "], + ['Name.Property', 'srt'], + ['Punctuation', ':'], + ['Text', ' Nothing'], + ['Punctuation', ')]'], + ['Text', "\n "], + ['Name.Entity', 'stack_info'], + ['Punctuation', ':'], + ['Text', ' '], + ['Name.Property', 'arg_space'], + ['Punctuation', ':'], + ['Text', ' '], + ['Literal.Number.Integer', '8'], + ['Text', ' '], + ['Name.Property', 'updfr_space'], + ['Punctuation', ':'], + ['Text', ' Just '], + ['Literal.Number.Integer', '8'], + ['Text', "\n"], + ['Punctuation', '}'] + end + + it 'should lex a ccall with hints' do + core = '(_c3zB::I64) = call "ccall" arg hints: [PtrHint, PtrHint] result hints: [PtrHint] newCAF(BaseReg, R1);' + + assert_tokens_equal core, + ['Punctuation', '('], + ['Name.Label', '_c3zB'], + ['Operator', '::'], + ['Keyword.Type', 'I64'], + ['Punctuation', ')'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Keyword', 'call'], + ['Text', ' '], + ['Literal.String.Delimiter', '"'], + ['Literal.String', 'ccall'], + ['Literal.String.Delimiter', '"'], + ['Text', ' '], + ['Name.Property', 'arg'], + ['Text', ' '], + ['Name.Property', 'hints'], + ['Punctuation', ':'], + ['Text', ' '], + ['Punctuation', '['], + ['Name.Label', 'PtrHint'], + ['Punctuation', ','], + ['Text', ' '], + ['Name.Label', 'PtrHint'], + ['Punctuation', ']'], + ['Text', ' '], + ['Name.Property', 'result'], + ['Text', ' '], + ['Name.Property', 'hints'], + ['Punctuation', ':'], + ['Text', ' '], + ['Punctuation', '['], + ['Name.Label', 'PtrHint'], + ['Punctuation', ']'], + ['Text', ' '], + ['Name.Function', 'newCAF'], + ['Punctuation', '('], + ['Name.Variable.Global', 'BaseReg'], + ['Punctuation', ','], + ['Text', ' '], + ['Name.Variable.Global', 'R1'], + ['Punctuation', ');'] + end + + it 'should lex escaped newlines' do + core = '#define SELECTOR_CODE_NOUPD(offset) \ +' + assert_tokens_equal core, + ['Comment.Preproc', '#define'], + ['Text', ' '], + ['Name.Label', 'SELECTOR_CODE_NOUPD'], + ['Punctuation', '('], + ['Name.Label', 'offset'], + ['Punctuation', ')'], + ['Text', " \\\n"] + end + + it 'should respect #define when it lexes types' do + core = '#define Char_hash_con_info _imp__ghczmprim_GHCziTypes_Czh_con_info +#define Int_hash_con_info _imp__ghczmprim_GHCziTypes_Izh_con_info' + + assert_tokens_equal core, + ['Comment.Preproc', '#define'], + ['Text', ' '], + ['Name.Label', 'Char_hash_con_info'], + ['Text', ' '], + ['Name.Label', '_imp__ghczmprim_GHCziTypes_Czh_con_info'], + ['Text', "\n"], + ['Comment.Preproc', '#define'], + ['Text', ' '], + ['Name.Label', 'Int_hash_con_info'], + ['Text', ' '], + ['Name.Label', '_imp__ghczmprim_GHCziTypes_Izh_con_info'] + end + + it 'should respect functions when it lexes types' do + core = 'SAVE_STGREGS +SAVE_THREAD_STATE();' + + assert_tokens_equal core, + ['Name.Label', 'SAVE_STGREGS'], + ['Text', "\n"], + ['Name.Function', 'SAVE_THREAD_STATE'], + ['Punctuation', '();'] + end + + it 'should lex inline function calls' do + core = 'StgTSO_alloc_limit(CurrentTSO) `lt` (0::I64)' + + assert_tokens_equal core, + ['Name.Function', 'StgTSO_alloc_limit'], + ['Punctuation', '('], + ['Name.Variable.Global', 'CurrentTSO'], + ['Punctuation', ')'], + ['Text', ' '], + ['Punctuation', '`'], + ['Name.Function', 'lt'], + ['Punctuation', '`'], + ['Text', ' '], + ['Punctuation', '('], + ['Literal.Number.Integer', '0'], + ['Operator', '::'], + ['Keyword.Type', 'I64'], + ['Punctuation', ')'] + end + + it 'should lex (codegen variables)' do + core = '' + + assert_tokens_equal core, + ['Name.Builtin', ''] + end + + it 'should lex special character ids in names with module prefix' do + core = 'GHC.Tuple.()_closure+1;' + + assert_tokens_equal core, + ['Name.Namespace', 'GHC'], + ['Punctuation', '.'], + ['Name.Namespace', 'Tuple'], + ['Punctuation', '.'], + ['Name.Label', '()_closure'], + ['Operator', '+'], + ['Literal.Number.Integer', '1'], + ['Punctuation', ';'] + end + + it 'should lex complex function names' do + core = 'foo()_(1);' + + assert_tokens_equal core, + ['Name.Function', 'foo()_'], + ['Punctuation', '('], + ['Literal.Number.Integer', '1'], + ['Punctuation', ');'] + end + + it 'should lex complex function names' do + core = 'foo()_(1);' + + assert_tokens_equal core, + ['Name.Function', 'foo()_'], + ['Punctuation', '('], + ['Literal.Number.Integer', '1'], + ['Punctuation', ');'] + + core = 'foo(,)3_(1);' + + assert_tokens_equal core, + ['Name.Function', 'foo(,)3_'], + ['Punctuation', '('], + ['Literal.Number.Integer', '1'], + ['Punctuation', ');'] + + core = '()_closure+1;' + + assert_tokens_equal core, + ['Name.Label', '()_closure'], + ['Operator', '+'], + ['Literal.Number.Integer', '1'], + ['Punctuation', ';'] + end + + it 'should lex complex names in an expression' do + core = 'const GHC.Types.[]_closure+1;' + + assert_tokens_equal core, + ['Keyword.Constant', 'const'], + ['Text', ' '], + ['Name.Namespace', 'GHC'], + ['Punctuation', '.'], + ['Name.Namespace', 'Types'], + ['Punctuation', '.'], + ['Name.Label', '[]_closure'], + ['Operator', '+'], + ['Literal.Number.Integer', '1'], + ['Punctuation', ';'] + + core = 'R1 = ()_closure+1;' + + assert_tokens_equal core, + ['Name.Variable.Global', 'R1'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Name.Label', '()_closure'], + ['Operator', '+'], + ['Literal.Number.Integer', '1'], + ['Punctuation', ';'] + end + end +end + + diff --git a/spec/visual/samples/ghc-cmm b/spec/visual/samples/ghc-cmm new file mode 100644 index 0000000000..1c047969c2 --- /dev/null +++ b/spec/visual/samples/ghc-cmm @@ -0,0 +1,625 @@ +/* + * Handwritten Code Examples + * Extracted from: https://gitlab.haskell.org/ghc/ghc/blob/master/ + */ + +#include "Cmm.h" +#include "Updates.h" +#include "SMPClosureOps.h" + +#if defined(__PIC__) +import pthread_mutex_unlock; +#endif +import EnterCriticalSection; +import LeaveCriticalSection; + +#define PRE_RETURN(why,what_next) \ + StgTSO_what_next(CurrentTSO) = what_next::I16; \ + StgRegTable_rRet(BaseReg) = why; \ + R1 = BaseReg; + +/* Remember that the return address is *removed* when returning to a + * ThreadRunGHC thread. + */ + +stg_gc_noregs +{ + W_ ret; + + DEBUG_ONLY(foreign "C" heapCheckFail()); + if (Hp > HpLim) { + Hp = Hp - HpAlloc/*in bytes*/; + if (HpLim == 0) { + ret = ThreadYielding; + goto sched; + } + if (HpAlloc <= BLOCK_SIZE + && bdescr_link(CurrentNursery) != NULL) { + HpAlloc = 0; + CLOSE_NURSERY(); + Capability_total_allocated(MyCapability()) = + Capability_total_allocated(MyCapability()) + + BYTES_TO_WDS(bdescr_free(CurrentNursery) - + bdescr_start(CurrentNursery)); + CurrentNursery = bdescr_link(CurrentNursery); + bdescr_free(CurrentNursery) = bdescr_start(CurrentNursery); + OPEN_NURSERY(); + if (Capability_context_switch(MyCapability()) != 0 :: CInt || + Capability_interrupt(MyCapability()) != 0 :: CInt || + (StgTSO_alloc_limit(CurrentTSO) `lt` (0::I64) && + (TO_W_(StgTSO_flags(CurrentTSO)) & TSO_ALLOC_LIMIT) != 0)) { + ret = ThreadYielding; + goto sched; + } else { + jump %ENTRY_CODE(Sp(0)) []; + } + } else { + ret = HeapOverflow; + goto sched; + } + } else { + if (CHECK_GC()) { + ret = HeapOverflow; + } else { + ret = StackOverflow; + } + } + sched: + PRE_RETURN(ret,ThreadRunGHC); + jump stg_returnToSched [R1]; +} + +#define HP_GENERIC \ + PRE_RETURN(HeapOverflow, ThreadRunGHC) \ + jump stg_returnToSched [R1]; + +#define BLOCK_GENERIC \ + PRE_RETURN(ThreadBlocked, ThreadRunGHC) \ + jump stg_returnToSched [R1]; + +#define YIELD_GENERIC \ + PRE_RETURN(ThreadYielding, ThreadRunGHC) \ + jump stg_returnToSched [R1]; + +#define BLOCK_BUT_FIRST(c) \ + PRE_RETURN(ThreadBlocked, ThreadRunGHC) \ + R2 = c; \ + jump stg_returnToSchedButFirst [R1,R2,R3]; + +#define YIELD_TO_INTERPRETER \ + PRE_RETURN(ThreadYielding, ThreadInterpret) \ + jump stg_returnToSchedNotPaused [R1]; + +/* ----------------------------------------------------------------------------- + Heap checks in thunks/functions. + + In these cases, node always points to the function closure. This gives + us an easy way to return to the function: just leave R1 on the top of + the stack, and have the scheduler enter it to return. + + There are canned sequences for 'n' pointer values in registers. + -------------------------------------------------------------------------- */ + +INFO_TABLE_RET ( stg_enter, RET_SMALL, W_ info_ptr, P_ closure ) + return (/* no return values */) +{ + ENTER(closure); +} + +__stg_gc_enter_1 (P_ node) +{ + jump stg_gc_noregs (stg_enter_info, node) (); +} + +/* ----------------------------------------------------------------------------- + Canned heap checks for primitives. + + We can't use stg_gc_fun because primitives are not functions, so + these fragments let us save some boilerplate heap-check-failure + code in a few common cases. + -------------------------------------------------------------------------- */ + +stg_gc_prim (W_ fun) +{ + call stg_gc_noregs (); + jump fun(); +} + +stg_gc_prim_p (P_ arg, W_ fun) +{ + call stg_gc_noregs (); + jump fun(arg); +} + +stg_gc_prim_pp (P_ arg1, P_ arg2, W_ fun) +{ + call stg_gc_noregs (); + jump fun(arg1,arg2); +} + +stg_gc_prim_n (W_ arg, W_ fun) +{ + call stg_gc_noregs (); + jump fun(arg); +} + +__stg_gc_fun /* explicit stack */ +{ + W_ size; + W_ info; + W_ type; + + info = %GET_FUN_INFO(UNTAG(R1)); + + // cache the size + type = TO_W_(StgFunInfoExtra_fun_type(info)); + if (type == ARG_GEN) { + size = BITMAP_SIZE(StgFunInfoExtra_bitmap(info)); + } else { + if (type == ARG_GEN_BIG) { +#if defined(TABLES_NEXT_TO_CODE) + // bitmap field holds an offset + size = StgLargeBitmap_size( + TO_W_(StgFunInfoExtraRev_bitmap_offset(info)) + + %GET_ENTRY(UNTAG(R1)) /* ### */ ); +#else + size = StgLargeBitmap_size( StgFunInfoExtra_bitmap(info) ); +#endif + } else { + size = BITMAP_SIZE(W_[stg_arg_bitmaps + WDS(type)]); + } + } + +#if defined(NO_ARG_REGS) + // we don't have to save any registers away + Sp_adj(-3); + Sp(2) = R1; + Sp(1) = size; + Sp(0) = stg_gc_fun_info; + jump stg_gc_noregs []; +#else + W_ type; + type = TO_W_(StgFunInfoExtra_fun_type(info)); + // cache the size + if (type == ARG_GEN || type == ARG_GEN_BIG) { + // regs already saved by the heap check code + Sp_adj(-3); + Sp(2) = R1; + Sp(1) = size; + Sp(0) = stg_gc_fun_info; + // DEBUG_ONLY(foreign "C" debugBelch("stg_fun_gc_gen(ARG_GEN)");); + jump stg_gc_noregs []; + } else { + jump W_[stg_stack_save_entries + WDS(type)] [*]; // all regs live + // jumps to stg_gc_noregs after saving stuff + } +#endif /* !NO_ARG_REGS */ +} + +stg_block_stmwait +{ + ccall stmWaitUnlock(MyCapability() "ptr", R3 "ptr"); + BLOCK_GENERIC; +} + +/* + * CLOSURE + */ + + +import CLOSURE ghczmprim_GHCziTypes_True_closure; +/* ---------------------------------------------------------------------------- + Dummy return closure + + Entering this closure will just return to the address on the top of the + stack. Useful for getting a thread in a canonical form where we can + just enter the top stack word to start the thread. (see deleteThread) + * ------------------------------------------------------------------------- */ + +INFO_TABLE( stg_dummy_ret, 0, 0, CONSTR_NOCAF, "DUMMY_RET", "DUMMY_RET") + () +{ + return (); +} +CLOSURE(stg_dummy_ret_closure,stg_dummy_ret); + + +#define SELECTOR_CODE_NOUPD(offset) \ + INFO_TABLE_SELECTOR(stg_sel_##offset##_noupd, offset, THUNK_SELECTOR, "stg_sel_noupd", "stg_sel_noupd") \ + (P_ node) \ + { \ + P_ selectee, field, dest; \ + TICK_ENT_DYN_THK(); \ + STK_CHK_NP(node); \ + UPD_BH_UPDATABLE(node); \ + LDV_ENTER(node); \ + selectee = StgThunk_payload(node,0); \ + ENTER_CCS_THUNK(node); \ + if (NEED_EVAL(selectee)) { \ + SAVE_CCS; \ + dest = UNTAG_IF_PROF(selectee); /* Note [untag for prof] */ \ + (P_ constr) = call %GET_ENTRY(dest) (dest); \ + RESTORE_CCS; \ + selectee = constr; \ + } \ + field = StgClosure_payload(UNTAG(selectee),offset); \ + jump stg_ap_0_fast(field); \ + } + +/* + * Generated Code Examples + */ + +==================== Output Cmm ==================== +2020-01-04 12:58:09.90905 UTC + +[] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.910624 UTC + +[section ""data" . Main.double_closure" { + Main.double_closure: + const GHC.Types.D#_con_info; + const 1.5 :: W64; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.91168 UTC + +[section ""data" . Main.character_closure" { + Main.character_closure: + const GHC.Types.C#_con_info; + const 99; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.912616 UTC + +[section ""cstring" . Main.string1_bytes" { + Main.string1_bytes: + I8[] [77,121,32,10,32,115,116,114,105,110,103,33] + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.918188 UTC + +[Main.string_entry() // [R1] + { info_tbls: [(c3zg, + label: Main.string_info + rep: HeapRep static { Thunk } + srt: Nothing)] + stack_info: arg_space: 8 updfr_space: Just 8 + } + {offset + c3zg: // global + if ((Sp + -16) < SpLim) (likely: False) goto c3zh; else goto c3zi; + c3zh: // global + R1 = R1; + call (stg_gc_enter_1)(R1) args: 8, res: 0, upd: 8; + c3zi: // global + (_c3zd::I64) = call "ccall" arg hints: [PtrHint, + PtrHint] result hints: [PtrHint] newCAF(BaseReg, R1); + if (_c3zd::I64 == 0) goto c3zf; else goto c3ze; + c3zf: // global + call (I64[R1])() args: 8, res: 0, upd: 8; + c3ze: // global + I64[Sp - 16] = stg_bh_upd_frame_info; + I64[Sp - 8] = _c3zd::I64; + R2 = Main.string1_bytes; + Sp = Sp - 16; + call GHC.CString.unpackCString#_info(R2) args: 24, res: 0, upd: 24; + } + }, + section ""data" . Main.string_closure" { + Main.string_closure: + const Main.string_info; + const 0; + const 0; + const 0; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.920067 UTC + +[section ""data" . Main.charArray3_closure" { + Main.charArray3_closure: + const GHC.Types.C#_con_info; + const 97; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.921058 UTC + +[section ""data" . Main.charArray2_closure" { + Main.charArray2_closure: + const GHC.Types.C#_con_info; + const 10; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.922009 UTC + +[section ""data" . Main.charArray1_closure" { + Main.charArray1_closure: + const :_con_info; + const Main.charArray2_closure+1; + const GHC.Types.[]_closure+1; + const 3; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.923223 UTC + +[section ""data" . Main.charArray_closure" { + Main.charArray_closure: + const :_con_info; + const Main.charArray3_closure+1; + const Main.charArray1_closure+2; + const 3; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.924127 UTC + +[section ""cstring" . Main.$trModule4_bytes" { + Main.$trModule4_bytes: + I8[] [109,97,105,110] + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.925047 UTC + +[section ""data" . Main.$trModule3_closure" { + Main.$trModule3_closure: + const GHC.Types.TrNameS_con_info; + const Main.$trModule4_bytes; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.926068 UTC + +[section ""cstring" . Main.$trModule2_bytes" { + Main.$trModule2_bytes: + I8[] [77,97,105,110] + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.926966 UTC + +[section ""data" . Main.$trModule1_closure" { + Main.$trModule1_closure: + const GHC.Types.TrNameS_con_info; + const Main.$trModule2_bytes; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.927956 UTC + +[section ""data" . Main.$trModule_closure" { + Main.$trModule_closure: + const GHC.Types.Module_con_info; + const Main.$trModule3_closure+1; + const Main.$trModule1_closure+1; + const 3; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.932229 UTC + +[section ""data" . u3zH_srt" { + u3zH_srt: + const stg_SRT_2_info; + const Main.string_closure; + const GHC.Show.showLitString_closure; + const 0; + }, + Main.main2_entry() // [R1] + { info_tbls: [(c3zE, + label: Main.main2_info + rep: HeapRep static { Thunk } + srt: Just u3zH_srt)] + stack_info: arg_space: 8 updfr_space: Just 8 + } + {offset + c3zE: // global + if ((Sp + -16) < SpLim) (likely: False) goto c3zF; else goto c3zG; + c3zF: // global + R1 = R1; + call (stg_gc_enter_1)(R1) args: 8, res: 0, upd: 8; + c3zG: // global + (_c3zB::I64) = call "ccall" arg hints: [PtrHint, + PtrHint] result hints: [PtrHint] newCAF(BaseReg, R1); + if (_c3zB::I64 == 0) goto c3zD; else goto c3zC; + c3zD: // global + call (I64[R1])() args: 8, res: 0, upd: 8; + c3zC: // global + I64[Sp - 16] = stg_bh_upd_frame_info; + I64[Sp - 8] = _c3zB::I64; + R3 = GHC.Show.$fShow[]1_closure; + R2 = Main.string_closure; + Sp = Sp - 16; + call GHC.Show.showLitString_info(R3, R2) args: 24, res: 0, upd: 24; + } + }, + section ""data" . Main.main2_closure" { + Main.main2_closure: + const Main.main2_info; + const 0; + const 0; + const 0; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.93387 UTC + +[section ""data" . Main.main1_closure" { + Main.main1_closure: + const :_con_info; + const GHC.Show.$fShow(,)3_closure; + const Main.main2_closure; + const 0; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.937028 UTC + +[Main.main_entry() // [] + { info_tbls: [(c3zU, + label: Main.main_info + rep: HeapRep static { Fun {arity: 1 fun_type: ArgSpec 3} } + srt: Nothing)] + stack_info: arg_space: 8 updfr_space: Just 8 + } + {offset + c3zU: // global + R4 = GHC.Types.True_closure+2; + R3 = Main.main1_closure+2; + R2 = GHC.IO.Handle.FD.stdout_closure; + call GHC.IO.Handle.Text.hPutStr'_info(R4, + R3, + R2) args: 8, res: 0, upd: 8; + } + }, + section ""data" . Main.main_closure" { + Main.main_closure: + const Main.main_info; + const GHC.IO.Handle.FD.stdout_closure; + const GHC.IO.Handle.Text.hPutStr'_closure; + const Main.main1_closure; + const 0; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.940434 UTC + +[Main.main3_entry() // [] + { info_tbls: [(c3A4, + label: Main.main3_info + rep: HeapRep static { Fun {arity: 1 fun_type: ArgSpec 3} } + srt: Nothing)] + stack_info: arg_space: 8 updfr_space: Just 8 + } + {offset + c3A4: // global + R2 = Main.main_closure+1; + call GHC.TopHandler.runMainIO1_info(R2) args: 8, res: 0, upd: 8; + } + }, + section ""data" . Main.main3_closure" { + Main.main3_closure: + const Main.main3_info; + const Main.main_closure; + const GHC.TopHandler.runMainIO1_closure; + const 0; + }] + + +==================== Output Cmm ==================== +2020-01-04 12:58:09.943474 UTC + +[:Main.main_entry() // [] + { info_tbls: [(c3Ae, + label: :Main.main_info + rep: HeapRep static { Fun {arity: 1 fun_type: ArgSpec 3} } + srt: Just Main.main3_closure)] + stack_info: arg_space: 8 updfr_space: Just 8 + } + {offset + c3Ae: // global + call Main.main3_info() args: 8, res: 0, upd: 8; + } + }, + section ""data" . :Main.main_closure" { + :Main.main_closure: + const :Main.main_info; + const 0; + }] + +==================== Output Cmm ==================== +2020-04-08 16:21:51.381258704 UTC + +[sat_s2eD_entry() { // [R2, R1] + { info_tbls: [(c2PW, + label: sat_s2eD_info + rep: HeapRep 1 nonptrs { Fun {arity: 1 fun_type: ArgSpec 5} } + srt: Nothing)] + stack_info: arg_space: 8 updfr_space: Just 8 + } + {offset + c2PW: // global + _s2eC::P64 = R2; + _s2eD::P64 = R1; + goto c2PY; + c2PY: // global + R1 = _s2eC::P64; + call stg_ap_0_fast(R1) args: 8, res: 0, upd: 8; + } + }, + sat_s2eN_entry() { // [R1] + { info_tbls: [(c2Q0, + label: sat_s2eN_info + rep: HeapRep 2 ptrs { Thunk } + srt: Just Data.Functor.Utils.#._closure)] + stack_info: arg_space: 8 updfr_space: Just 8 + } + {offset + c2Q0: // global + _s2eN::P64 = R1; + if ((Sp + 8) - 24 < SpLim) (likely: False) goto c2Q1; else goto c2Q2; + c2Q2: // global + Hp = Hp + 56; + if (Hp > HpLim) (likely: False) goto c2Q4; else goto c2Q3; + c2Q4: // global + HpAlloc = 56; + goto c2Q1; + c2Q1: // global + R1 = _s2eN::P64; + call (stg_gc_enter_1)(R1) args: 8, res: 0, upd: 8; + c2Q3: // global + I64[Sp - 16] = stg_upd_frame_info; + P64[Sp - 8] = _s2eN::P64; + _s2el::P64 = P64[_s2eN::P64 + 16]; + _s2en::P64 = P64[_s2eN::P64 + 24]; + I64[Hp - 48] = stg_sel_6_upd_info; + P64[Hp - 32] = _s2en::P64; + _c2PQ::P64 = Hp - 48; + I64[Hp - 24] = sat_s2eD_info; + _c2PR::P64 = Hp - 23; + I64[Hp - 8] = GHC.Types.MkCoercible_con_info; + _c2PZ::P64 = Hp - 7; + R5 = _c2PQ::P64; + R4 = _s2el::P64; + R3 = _c2PR::P64; + R2 = _c2PZ::P64; + R1 = Data.Functor.Utils.#._closure; + Sp = Sp - 16; + call stg_ap_pppp_fast(R5, + R4, + R3, + R2, + R1) args: 24, res: 0, upd: 24; + } + }, + section ""data" . $cbifoldr1_r29S_closure" { + $cbifoldr1_r29S_closure: + const $cbifoldr1_r29S_info; + const 0; + }]