![]() System : Linux absol.cf 5.4.0-198-generic #218-Ubuntu SMP Fri Sep 27 20:18:53 UTC 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.33 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, Directory : /usr/share/emscripten/src/ |
Upload File : |
//"use strict"; // LLVM assembly => internal intermediate representation, which is ready // to be processed by the later stages. var fastPaths = 0, slowPaths = 0; var tokenCache = {}; [',', '{', '}', 'i32', 'label', ';', '4', '0', '1', '2', '255', 'align', 'i8*', 'i8', 'i16', 'getelementptr', 'inbounds', 'unnamed_addr', 'x', 'load', 'preds', 'br', 'i32*', 'i1', 'store', '<label>', 'constant', 'c', 'private', 'null', 'internal', 'to', 'bitcast', 'define', 'nounwind', 'nocapture', '%this', 'call', '...'].forEach(function(text) { tokenCache[text] = { text: text } }); //var tokenCacheMisses = {}; // Line tokenizer function tokenize(text) { var tokens = []; var quotes = 0; var lastToken = null; var enclosers = { '[': 0, ']': '[', '(': 0, ')': '(', '<': 0, '>': '<', '{': 0, '}': '{', }; var totalEnclosing = 0; function makeToken(text) { if (text.length == 0) return; // merge *..* into last token if (lastToken && text[0] === '*') { //assert(/^\**$/.test(text)); //assert(!(lastToken.text in tokenCache)); lastToken.text += text; return; } var cached = tokenCache[text]; if (cached) { //assert(cached.text === text); tokens.push(cached); lastToken = cached; return; } //tokenCacheMisses[text] = (misses[text] || 0) + 1; var token = { text: text }; if (text[0] in enclosers) { token.tokens = tokenize(text.substr(1, text.length-2)); token.type = text[0]; } // merge function definitions together if (lastToken && isType(lastToken.text) && isFunctionDef(token)) { if (lastToken.text in tokenCache) { // create a copy of the cached value lastToken = tokens[tokens.length-1] = { text: lastToken.text }; } lastToken.text += ' ' + text; } else { tokens.push(token); lastToken = token; } } // Split using meaningful characters var lineText = text + ' '; var re = /[\[\]\(\)<>{}, "]/g; var segments = lineText.split(re); segments.pop(); var len = segments.length; var i = -1; var start = 0; var segment, letter, last; if (lineText[0] === '}') { tokens.push(tokenCache['}']); // end of function and landingpads have an unmatched { } for (var s = 0; s < len; s++) { segment = segments[s]; i += segment.length + 1; last = letter; letter = lineText[i]; switch (letter) { case ' ': if (totalEnclosing == 0 && quotes == 0) { makeToken(lineText.substring(start, i)); start = i+1; } break; case '"': if (totalEnclosing == 0) { if (quotes == 0) { if (start === i-1 && (lineText[start] == '@' || lineText[start] == '%')) { } else { makeToken(lineText.substring(start, i)); start = i; } } else { makeToken(lineText.substring(start, i+1)); start = i+1; } } quotes = 1-quotes; break; case ',': if (totalEnclosing == 0 && quotes == 0) { makeToken(lineText.substring(start, i)); start = i+1; tokens.push(tokenCache[',']); } break; default: assert(letter in enclosers); if (quotes) { break; } if (letter in ENCLOSER_STARTERS) { if (totalEnclosing == 0) { makeToken(lineText.substring(start, i)); start = i; } enclosers[letter]++; totalEnclosing++; } else { enclosers[enclosers[letter]]--; totalEnclosing--; if (totalEnclosing == 0) { makeToken(lineText.substring(start, i+1)); start = i+1; } } } } if (last === '{') { tokens.push(tokenCache['{']); // beginning of function and landingpads have an unmatched { } return tokens; } // Handy sets var ENCLOSER_STARTERS = set('[', '(', '<', '{'); var ENCLOSER_ENDERS = { '[': ']', '(': ')', '<': '>', '{': '}' }; var ZEROINIT_UNDEF = set('zeroinitializer', 'undef'); var NSW_NUW = set('nsw', 'nuw'); // Intertyper function intertyper(lines, sidePass, baseLineNums) { //B.start('intertyper'); var mainPass = !sidePass; baseLineNums = baseLineNums || [[0,0]]; // each pair [#0,#1] means "starting from line #0, the base line num is #1" dprint('framework', 'Big picture: Starting intertyper, main pass=' + mainPass); var finalResults = []; // Line splitter. We break off some bunches of lines into unparsed bundles, which are // parsed in separate passes later. This helps to keep memory usage low - we can start // from raw lines and end up with final JS for each function individually that way, instead // of intertyping them all, then analyzing them all, etc. function lineSplitter() { var ret = []; var inContinual = false; var inFunction = false; var currFunctionLines; var currFunctionLineNum; var unparsedTypes, unparsedGlobals; if (mainPass) { unparsedTypes = { intertype: 'unparsedTypes', lines: [] }; finalResults.push(unparsedTypes); unparsedGlobals = { intertype: 'unparsedGlobals', lines: [] }; finalResults.push(unparsedGlobals); } var baseLineNumPosition = 0; for (var i = 0; i < lines.length; i++) { var line = lines[i]; if (singlePhase) lines[i] = null; // lines may be very very large. Allow GCing to occur in the loop by releasing refs here while (baseLineNumPosition < baseLineNums.length-1 && i >= baseLineNums[baseLineNumPosition+1][0]) { baseLineNumPosition++; } if (mainPass && (line[0] == '%' || line[0] == '@')) { // If this isn't a type, it's a global variable, make a note of the information now, we will need it later var parts = line.split(' = '); assert(parts.length >= 2); var left = parts[0], right = parts.slice(1).join(' = '); var testType = /^type .*/.exec(right); if (!testType) { var globalIdent = toNiceIdent(left); var testAlias = /^(hidden )?alias .*/.exec(right); Variables.globals[globalIdent] = { name: globalIdent, alias: !!testAlias, impl: VAR_EMULATED }; unparsedGlobals.lines.push(line); } else { unparsedTypes.lines.push(line); } continue; } if (mainPass && /^define .*/.test(line)) { inFunction = true; currFunctionLines = []; currFunctionLineNum = i + 1; } if (!inFunction || !mainPass) { if (inContinual || /^\ +(to|catch |filter |cleanup).*/.test(line)) { // to after invoke or landingpad second line ret.slice(-1)[0].lineText += line; if (/^\ +\]/.test(line)) { // end of llvm switch inContinual = false; } } else { ret.push({ lineText: line, lineNum: i + 1 + baseLineNums[baseLineNumPosition][1] - baseLineNums[baseLineNumPosition][0] }); if (/^\ +switch\ .*/.test(line)) { // beginning of llvm switch inContinual = true; } } } else { currFunctionLines.push(line); } if (mainPass && /^}.*/.test(line)) { inFunction = false; if (mainPass) { var func = funcHeaderHandler({ tokens: tokenize(currFunctionLines[0]) }); if (SKIP_STACK_IN_SMALL && /emscripten_autodebug/.exec(func.ident)) { warnOnce('Disabling SKIP_STACK_IN_SMALL because we are apparently processing autodebugger data'); SKIP_STACK_IN_SMALL = 0; } var ident = toNiceIdent(func.ident); if (!(ident in DEAD_FUNCTIONS)) { finalResults.push({ intertype: 'unparsedFunction', // We need this early, to know basic function info - ident, params, varargs ident: ident, params: func.params, returnType: func.returnType, hasVarArgs: func.hasVarArgs, lineNum: currFunctionLineNum, lines: currFunctionLines }); } currFunctionLines = []; } } } // We need lines beginning with ';' inside functions, because older LLVM versions generated labels that way. But when not // parsing functions, we can ignore all such lines and save some time that way. return ret.filter(function(item) { return item.lineText && (item.lineText[0] != ';' || !mainPass); }); } function triager(item) { assert(!item.intertype); var token0Text = item.tokens[0].text; var token1Text = item.tokens[1] ? item.tokens[1].text : null; var tokensLength = item.tokens.length; if (item.indent === 2) { if (tokensLength >= 5 && (token0Text == 'store' || token1Text == 'store')) return storeHandler(item); if (tokensLength >= 3 && token0Text == 'br') return branchHandler(item); if (tokensLength >= 2 && token0Text == 'ret') return returnHandler(item); if (tokensLength >= 2 && token0Text == 'switch') return switchHandler(item); if (token0Text == 'unreachable') return unreachableHandler(item); if (tokensLength >= 3 && token0Text == 'indirectbr') return indirectBrHandler(item); if (tokensLength >= 2 && token0Text == 'resume') return resumeHandler(item); if (tokensLength >= 3 && (token0Text == 'load' || token1Text == 'load')) return loadHandler(item); if (tokensLength >= 3 && token0Text in MATHOPS) return mathopsHandler(item); if (tokensLength >= 3 && token0Text == 'bitcast') return bitcastHandler(item); if (tokensLength >= 3 && token0Text == 'getelementptr') return GEPHandler(item); if (tokensLength >= 2 && token0Text == 'alloca') return allocaHandler(item); if (tokensLength >= 3 && token0Text == 'extractvalue') return extractValueHandler(item); if (tokensLength >= 3 && token0Text == 'insertvalue') return insertValueHandler(item); if (tokensLength >= 3 && token0Text == 'phi') return phiHandler(item); if (tokensLength >= 3 && token0Text == 'va_arg') return va_argHandler(item); if (tokensLength >= 3 && token0Text == 'landingpad') return landingpadHandler(item); if (token0Text === 'insertelement') return insertElementHandler(item); if (token0Text === 'extractelement') return extractElementHandler(item); if (token0Text === 'shufflevector') return shuffleVectorHandler(item); if (token0Text == 'fence') return null; } else if (item.indent === 0) { if ((tokensLength >= 1 && token0Text.substr(-1) == ':') || (tokensLength >= 3 && token1Text == '<label>') || (tokensLength >= 2 && token1Text == ':')) return labelHandler(item); if (tokensLength >= 4 && token0Text == 'declare') return externalHandler(item); if (item.assignTo) return globalHandler(item); if (tokensLength >= 4 && token0Text == 'define' && item.tokens.slice(-1)[0].text == '{') return funcHeaderHandler(item); if (tokensLength >= 1 && token0Text == '}') return funcEndHandler(item); if (token0Text == 'module' && token1Text == 'asm') { warn('Ignoring module asm: ' + item.tokens[2].text); return null; } if (token0Text == 'attributes') return null; } if (tokensLength >= 3 && (token0Text == 'call' || token1Text == 'call')) return callHandler(item); if (token0Text == 'target') { if (token1Text == 'triple') { var triple = item.tokens[3].text; triple = triple.substr(1, triple.length-2); var expected = TARGET_ASMJS_UNKNOWN_EMSCRIPTEN ? 'asmjs-unknown-emscripten' : 'i386-pc-linux-gnu'; if (triple !== expected) { if (!(TARGET_ASMJS_UNKNOWN_EMSCRIPTEN && triple === 'le32-unknown-nacl')) { warn('using an unexpected LLVM triple: ' + [triple, ' !== ', expected] + ' (are you using emcc for everything and not clang?)'); } } } return null; } if (token0Text == ';') return null; if (tokensLength >= 3 && token0Text == 'invoke') return invokeHandler(item); if (tokensLength >= 3 && token0Text == 'atomicrmw' || token0Text == 'cmpxchg') return atomicHandler(item); throw 'Invalid token, cannot triage: ' + dump(item); } // Line parsers to intermediate form // globals: type or variable function noteGlobalVariable(ret) { if (!NAMED_GLOBALS) { Variables.globals[ret.ident].type = ret.type; Variables.globals[ret.ident].external = ret.external; } Types.needAnalysis[ret.type] = 0; } function globalHandler(item) { function scanConst(value, type) { // Gets an array of constant items, separated by ',' tokens function handleSegments(tokens) { // Handle a single segment (after comma separation) function handleSegment(segment) { if (segment[1].text == 'null') { return { intertype: 'value', ident: '0', type: 'i32' }; } else if (segment[1].text == 'zeroinitializer') { Types.needAnalysis[segment[0].text] = 0; return { intertype: 'emptystruct', type: segment[0].text }; } else if (segment[1].text in PARSABLE_LLVM_FUNCTIONS) { return parseLLVMFunctionCall(segment); } else if (segment[1].type && segment[1].type == '{') { Types.needAnalysis[segment[0].text] = 0; return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].tokens) }; } else if (segment[1].type && segment[1].type == '<') { Types.needAnalysis[segment[0].text] = 0; return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].tokens[0].tokens) }; } else if (segment[1].type && segment[1].type == '[') { Types.needAnalysis[segment[0].text] = 0; return { intertype: 'list', type: segment[0].text, contents: handleSegments(segment[1].tokens) }; } else if (segment.length == 2) { Types.needAnalysis[segment[0].text] = 0; return { intertype: 'value', type: segment[0].text, ident: toNiceIdent(segment[1].text) }; } else if (segment[1].text === 'c') { // string var text = segment[2].text; text = text.substr(1, text.length-2); return { intertype: 'string', text: text, type: 'i8*' }; } else if (segment[1].text === 'blockaddress') { return parseBlockAddress(segment); } else { throw 'Invalid segment: ' + dump(segment); } }; return splitTokenList(tokens).map(handleSegment); } Types.needAnalysis[type] = 0; if (Runtime.isNumberType(type) || pointingLevels(type) >= 1) { return { value: toNiceIdent(value.text), type: type }; } else if (value.text in ZEROINIT_UNDEF) { // undef doesn't really need initting, but why not return { intertype: 'emptystruct', type: type }; } else if (value.text && value.text[0] == '"') { return { intertype: 'string', text: value.text.substr(1, value.text.length-2) }; } else { if (value.type == '<') { // <{ i8 }> etc. value = value.tokens; } var contents; if (value.type && value.type !== '{') { // list of items contents = value.tokens; } else if (value.type == '{') { // struct contents = value.tokens; } else if (value[0]) { contents = value[0]; } else { throw '// interfailzzzzzzzzzzzzzz ' + dump(value); } return { intertype: 'segments', contents: handleSegments(contents) }; } } cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 0); if (item.tokens[0].text == 'alias') { cleanOutTokens(LLVM.LINKAGES, item.tokens, 1); cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 1); var last = getTokenIndexByText(item.tokens, ';'); var ret = { intertype: 'alias', ident: item.assignTo, value: parseLLVMSegment(item.tokens.slice(1, last)), lineNum: item.lineNum }; ret.type = ret.value.type; Types.needAnalysis[ret.type] = 0; if (!NAMED_GLOBALS) { Variables.globals[ret.ident].type = ret.type; } return ret; } if (item.tokens[0].text == 'type') { var fields = []; var packed = false; if (Runtime.isNumberType(item.tokens[1].text)) { // Clang sometimes has |= i32| instead of |= { i32 }| fields = [item.tokens[1].text]; } else if (item.tokens[1].text != 'opaque') { if (item.tokens[1].type == '<') { packed = true; item.tokens[1] = item.tokens[1].tokens[0]; } var subTokens = item.tokens[1].tokens; if (subTokens && subTokens.length > 0) { subTokens.push({text:','}); // XXX we should avoid altering tokens like that while (subTokens[0]) { var stop = 1; while ([','].indexOf(subTokens[stop].text) == -1) stop ++; fields.push(combineTokens(subTokens.slice(0, stop)).text); subTokens.splice(0, stop+1); } } } return { intertype: 'type', name_: item.assignTo, fields: fields, packed: packed, lineNum: item.lineNum }; } else { // variable var ident = item.assignTo; var private_ = findTokenText(item, 'private') >= 0 || findTokenText(item, 'internal') >= 0; var named = findTokenText(item, 'unnamed_addr') < 0; cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [0, 1]); var external = false; if (item.tokens[0].text === 'external') { external = true; item.tokens.splice(0, 1); } var ret = { intertype: 'globalVariable', ident: ident, type: item.tokens[0].text, external: external, private_: private_, named: named, lineNum: item.lineNum }; noteGlobalVariable(ret); if (ident == '_llvm_global_ctors') { ret.ctors = []; var subTokens = item.tokens[1].tokens; if (subTokens) { splitTokenList(subTokens).forEach(function(segment) { var parsed = parseLLVMSegment(segment); assert(parsed.intertype === 'structvalue'); var ctor = toNiceIdent(parsed.params[1].ident); ret.ctors.push(ctor); if (ASM_JS) { // must export the global constructors from asm.js module, so mark as implemented and exported Functions.implementedFunctions[ctor] = 'v'; EXPORTED_FUNCTIONS[ctor] = 1; } }); } } else if (ident == '_llvm_used') { var chunk = item.tokens[1].tokens; var funcs = []; var part = []; for (var i = 0; i < chunk.length; i++) { if (chunk[i].text == ',') { var call = parseLLVMFunctionCall(part); EXPORTED_FUNCTIONS[call.ident] = 0; part = []; } else { part.push(chunk[i]); } } if (part.length > 0) { var call = parseLLVMFunctionCall(part); EXPORTED_FUNCTIONS[call.ident] = 0; } ret.type = 'i32'; ret.value = { intertype: 'value', ident: '0', value: '0', type: ret.type }; } else if (!external) { if (item.tokens[1] && item.tokens[1].text != ';') { if (item.tokens[1].text == 'c') { item.tokens.splice(1, 1); } if (item.tokens[1].text in PARSABLE_LLVM_FUNCTIONS) { ret.value = parseLLVMFunctionCall(item.tokens); } else { ret.value = scanConst(item.tokens[1], ret.type); } } else { ret.value = { intertype: 'value', ident: '0', value: '0', type: ret.type }; } } return ret; } } // function header function funcHeaderHandler(item) { item.tokens = item.tokens.filter(function(token) { return !(token.text in LLVM.LINKAGES || token.text in LLVM.PARAM_ATTR || token.text in LLVM.FUNC_ATTR || token.text in LLVM.CALLING_CONVENTIONS); }); var params = parseParamTokens(item.tokens[2].tokens); if (sidePass) dprint('unparsedFunctions', 'Processing function: ' + item.tokens[1].text); return { intertype: 'function', ident: toNiceIdent(item.tokens[1].text), returnType: item.tokens[0].text, params: params, hasVarArgs: hasVarArgs(params), lineNum: item.lineNum, }; } // label function labelHandler(item) { var rawLabel = item.tokens[0].text.substr(-1) == ':' ? '%' + item.tokens[0].text.substr(0, item.tokens[0].text.length-1) : (item.tokens[1].text == '<label>' ? '%' + item.tokens[2].text.substr(1) : '%' + item.tokens[0].text) var niceLabel = toNiceIdent(rawLabel); return { intertype: 'label', ident: niceLabel, lineNum: item.lineNum }; } // 'load' function loadHandler(item) { item.intertype = 'load'; cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]); item.pointerType = item.tokens[1].text; item.valueType = item.type = removePointing(item.pointerType); Types.needAnalysis[item.type] = 0; var last = getTokenIndexByText(item.tokens, ';'); var segments = splitTokenList(item.tokens.slice(1, last)); item.pointer = parseLLVMSegment(segments[0]); if (segments.length > 1) { assert(segments[1][0].text == 'align'); item.align = parseInt(segments[1][1].text) || QUANTUM_SIZE; // 0 means preferred arch align } else { item.align = QUANTUM_SIZE; } item.ident = item.pointer.ident || null; return item; } // 'extractvalue' function extractValueHandler(item) { var last = getTokenIndexByText(item.tokens, ';'); item.intertype = 'extractvalue'; item.type = item.tokens[1].text; // Of the origin aggregate - not what we extract from it. For that, can only infer it later Types.needAnalysis[item.type] = 0; item.ident = toNiceIdent(item.tokens[2].text); item.indexes = splitTokenList(item.tokens.slice(4, last)); return item; } // 'insertvalue' function insertValueHandler(item) { var last = getTokenIndexByText(item.tokens, ';'); item.intertype = 'insertvalue'; item.type = item.tokens[1].text; // Of the origin aggregate, as well as the result Types.needAnalysis[item.type] = 0; item.ident = toNiceIdent(item.tokens[2].text); var segments = splitTokenList(item.tokens.slice(4, last)); item.value = parseLLVMSegment(segments[0]); item.indexes = segments.slice(1); return item; } // 'bitcast' function bitcastHandler(item) { item.intertype = 'bitcast'; var last = getTokenIndexByText(item.tokens, ';'); item.type = item.tokens[Math.min(last, item.tokens.length-1)].text; // The final type Types.needAnalysis[item.type] = 0; var to = getTokenIndexByText(item.tokens, 'to'); item.params = [parseLLVMSegment(item.tokens.slice(1, to))]; item.ident = item.params[0].ident; item.type2 = item.tokens[1].text; // The original type Types.needAnalysis[item.type2] = 0; return item; } // 'getelementptr' function GEPHandler(item) { var first = 0; while (!isType(item.tokens[first].text)) first++; Types.needAnalysis[item.tokens[first].text] = 0; var last = getTokenIndexByText(item.tokens, ';'); var segment = [ item.tokens[first], { text: 'getelementptr' }, null, { tokens: item.tokens.slice(first, last) } ]; var data = parseLLVMFunctionCall(segment); item.intertype = 'getelementptr'; item.type = '*'; // We need type info to determine this - all we know is it's a pointer item.params = data.params; item.ident = data.ident; return item; } // 'call', 'invoke' function makeCall(item, type) { item.intertype = type; if (['tail'].indexOf(item.tokens[0].text) != -1) { item.tokens.splice(0, 1); } while (item.tokens[1].text in LLVM.PARAM_ATTR || item.tokens[1].text in LLVM.CALLING_CONVENTIONS) { item.tokens.splice(1, 1); } item.type = item.tokens[1].text; Types.needAnalysis[item.type] = 0; while (['@', '%'].indexOf(item.tokens[2].text[0]) == -1 && !(item.tokens[2].text in PARSABLE_LLVM_FUNCTIONS) && item.tokens[2].text != 'null' && item.tokens[2].text != 'asm' && item.tokens[2].text != 'undef') { assert(item.tokens[2].text != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); item.tokens.splice(2, 1); } var tokensLeft = item.tokens.slice(2); item.ident = eatLLVMIdent(tokensLeft); if (item.ident == 'asm') { warnOnce('inline JavaScript using asm() has some oddities due to how gcc asm() syntax works. use EM_ASM where possible (see emscripten.h)'); if (ASM_JS) { Types.hasInlineJS = true; warnOnce('inline JavaScript using asm() will cause the code to no longer fall in the asm.js subset of JavaScript, which can reduce performance - consider using emscripten_run_script'); } assert(TARGET_ASMJS_UNKNOWN_EMSCRIPTEN, 'inline js is only supported in asmjs-unknown-emscripten'); // Inline assembly is just JavaScript that we paste into the code item.intertype = 'value'; if (tokensLeft[0].text == 'sideeffect') tokensLeft.splice(0, 1); item.ident = tokensLeft[0].text.substr(1, tokensLeft[0].text.length-2) || ';'; // use ; for empty inline assembly assert((item.tokens[5].text.match(/=/g) || []).length <= 1, 'we only support at most 1 exported variable from inline js: ' + item.ident); var i = 0; var params = [], args = []; if (tokensLeft[3].tokens) { splitTokenList(tokensLeft[3].tokens).map(function(element) { var ident = toNiceIdent(element[1].text); var type = element[0].text; params.push('$' + (i++)); args.push(ident); }); } item.ident = expandLLVMString(item.ident).replace(/(#[^\n]*)/g, function(m) { return '/* ' + m.substr(1) + ' */'; // fix asm comments to js comments }); if (item.assignTo) item.ident = 'return ' + item.ident; item.ident = '(function(' + params + ') { ' + item.ident + ' })(' + args + ');'; return { ret: item, item: item }; } if (item.ident.substr(-2) == '()') { // See comment in isStructType() item.ident = item.ident.substr(0, item.ident.length-2); // Also, we remove some spaces which might occur. while (item.ident[item.ident.length-1] == ' ') { item.ident = item.ident.substr(0, item.ident.length-1); } item.params = []; } else { item.params = parseParamTokens(tokensLeft[0].tokens); } item.ident = toNiceIdent(item.ident); if (type === 'invoke') { var toIndex = findTokenText(item, 'to'); item.toLabel = toNiceIdent(item.tokens[toIndex+2].text); item.unwindLabel = toNiceIdent(item.tokens[toIndex+5].text); assert(item.toLabel && item.unwindLabel); } if (item.indent == 2) { // standalone call - not in assign item.standalone = true; return { ret: item, item: item }; } return { ret: null, item: item }; } function callHandler(item) { var result = makeCall.call(this, item, 'call'); return result.ret; } function invokeHandler(item) { var result = makeCall.call(this, item, 'invoke'); if (DISABLE_EXCEPTION_CATCHING == 1) { result.item.intertype = 'call'; finalResults.push({ intertype: 'branch', label: result.item.toLabel, lineNum: item.lineNum + 0.5 }); } return result.ret; } function atomicHandler(item) { item.intertype = 'atomic'; if (item.tokens[0].text == 'atomicrmw') { if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1); item.op = item.tokens[1].text; item.tokens.splice(1, 1); } else { assert(item.tokens[0].text == 'cmpxchg') if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1); item.op = 'cmpxchg'; } var last = getTokenIndexByText(item.tokens, ';'); item.params = splitTokenList(item.tokens.slice(1, last)).map(parseLLVMSegment); item.type = item.params[1].type; return item; } // 'landingpad' function landingpadHandler(item) { item.intertype = 'landingpad'; item.type = item.tokens[1].text; item.catchables = []; var catchIdx = findTokenText(item, "catch"); if (catchIdx != -1) { do { var nextCatchIdx = findTokenTextAfter(item, "catch", catchIdx+1); if (nextCatchIdx == -1) nextCatchIdx = item.tokens.length; item.catchables.push(parseLLVMSegment(item.tokens.slice(catchIdx+2, nextCatchIdx))); catchIdx = nextCatchIdx; } while (catchIdx != item.tokens.length); } Types.needAnalysis[item.type] = 0; return item; } function insertElementHandler(item) { var last = getTokenIndexByText(item.tokens, ';'); item.intertype = 'insertelement'; item.type = item.tokens[1].text; item.ident = toNiceIdent(item.tokens[2].text); var segments = splitTokenList(item.tokens.slice(4, last)); item.value = parseLLVMSegment(segments[0]); item.index = parseLLVMSegment(segments[1]); return item; } function extractElementHandler(item) { var last = getTokenIndexByText(item.tokens, ';'); item.intertype = 'extracttelement'; item.type = item.tokens[1].text; item.ident = toNiceIdent(item.tokens[2].text); item.value = parseLLVMSegment(item.tokens.slice(4)); return item; } function shuffleVectorHandler(item) { var last = getTokenIndexByText(item.tokens, ';'); item.intertype = 'shufflevector'; item.type = item.tokens[1].text; // Of the origin aggregate, as well as the result Types.needAnalysis[item.type] = 0; item.ident = toNiceIdent(item.tokens[2].text); var segments = splitTokenList(item.tokens.slice(4, last)); item.value = parseLLVMSegment(segments[0]); item.mask = parseLLVMSegment(segments[1]); return item; } // 'alloca' function allocaHandler(item) { item.intertype = 'alloca'; item.allocatedType = item.tokens[1].text; if (item.tokens.length > 3 && Runtime.isNumberType(item.tokens[3].text)) { item.ident = toNiceIdent(item.tokens[4].text); } else { item.ident = 1; } item.type = addPointing(item.tokens[1].text); // type of pointer we will get Types.needAnalysis[item.type] = 0; item.type2 = item.tokens[1].text; // value we will create, and get a pointer to Types.needAnalysis[item.type2] = 0; return item; } // 'phi' function phiHandler(item) { item.intertype = 'phi'; item.type = item.tokens[1].text; var typeToken = [item.tokens[1]]; Types.needAnalysis[item.type] = 0; var last = getTokenIndexByText(item.tokens, ';'); item.params = splitTokenList(item.tokens.slice(2, last)).map(function(segment) { var subSegments = splitTokenList(segment[0].tokens); var ret = { intertype: 'phiparam', label: toNiceIdent(subSegments[1][0].text), value: parseLLVMSegment(typeToken.concat(subSegments[0])) }; return ret; }).filter(function(param) { return param.value && param.value.ident != 'undef' }); return item; } // 'va_arg' function va_argHandler(item) { item.intertype = 'va_arg'; var segments = splitTokenList(item.tokens.slice(1)); item.type = segments[1][0].text; item.value = parseLLVMSegment(segments[0]); return item; } // mathops function mathopsHandler(item) { item.intertype = 'mathop'; item.op = item.tokens[0].text; item.variant = null; while (item.tokens[1].text in NSW_NUW) item.tokens.splice(1, 1); if (['icmp', 'fcmp'].indexOf(item.op) != -1) { item.variant = item.tokens[1].text; item.tokens.splice(1, 1); } while (item.tokens[1].text in LLVM.MATHOP_IGNORABLES) item.tokens.splice(1, 1); var segments = splitTokenList(item.tokens.slice(1)); item.params = []; for (var i = 1; i <= 4; i++) { if (segments[i-1]) { if (i > 1 && segments[i-1].length == 1 && segments[0].length > 1 && !isType(segments[i-1][0].text)) { segments[i-1].unshift(segments[0][0]); // Add the type from the first segment, they are all alike } item.params[i-1] = parseLLVMSegment(segments[i-1]); } } var setParamTypes = true; if (item.op === 'select') { assert(item.params[1].type === item.params[2].type); item.type = item.params[1].type; } else if (item.op in LLVM.CONVERSIONS) { item.type = item.params[1].type; setParamTypes = false; } else { item.type = item.params[0].type; } if (setParamTypes) { for (var i = 0; i < 4; i++) { if (item.params[i]) item.params[i].type = item.type; // All params have the same type, normally } } if (item.op in LLVM.EXTENDS) { item.type = item.params[1].ident; item.params[0].type = item.params[1].type; // TODO: also remove 2nd param? } else if (item.op in LLVM.COMPS) { item.type = 'i1'; if (item.params[1].intertype === 'type') item.params[1].intertype = 'value'; // parsed as type, but comparisons have just values there } if (USE_TYPED_ARRAYS == 2) { // Some specific corrections, since 'i64' is special if (item.op in LLVM.SHIFTS) { item.params[1].type = 'i32'; } else if (item.op == 'select') { item.params[0].type = 'i1'; } } Types.needAnalysis[item.type] = 0; return item; } // 'store' function storeHandler(item) { cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]); var segments = splitTokenList(item.tokens.slice(1)); var ret = { intertype: 'store', valueType: item.tokens[1].text, value: parseLLVMSegment(segments[0]), pointer: parseLLVMSegment(segments[1]), lineNum: item.lineNum }; Types.needAnalysis[ret.valueType] = 0; ret.ident = toNiceIdent(ret.pointer.ident); ret.pointerType = ret.pointer.type; Types.needAnalysis[ret.pointerType] = 0; if (segments.length > 2) { assert(segments[2][0].text == 'align'); ret.align = parseInt(segments[2][1].text) || QUANTUM_SIZE; // 0 means preferred arch align } else { ret.align = QUANTUM_SIZE; } return ret; } // 'br' function branchHandler(item) { if (item.tokens[1].text == 'label') { return { intertype: 'branch', label: toNiceIdent(item.tokens[2].text), lineNum: item.lineNum }; } else { var commaIndex = findTokenText(item, ','); return { intertype: 'branch', value: parseLLVMSegment(item.tokens.slice(1, commaIndex)), labelTrue: toNiceIdent(item.tokens[commaIndex+2].text), labelFalse: toNiceIdent(item.tokens[commaIndex+5].text), lineNum: item.lineNum }; } } // 'ret' function returnHandler(item) { var type = item.tokens[1].text; Types.needAnalysis[type] = 0; return { intertype: 'return', type: type, value: (item.tokens[2] && type !== 'void') ? parseLLVMSegment(item.tokens.slice(1)) : null, lineNum: item.lineNum }; } // 'resume' - partial implementation function resumeHandler(item) { return { intertype: 'resume', ident: toNiceIdent(item.tokens[2].text), lineNum: item.lineNum }; } // 'switch' function switchHandler(item) { function parseSwitchLabels(item) { var ret = []; var tokens = item.tokens; while (tokens.length > 0) { ret.push({ value: tokens[1].text, label: toNiceIdent(tokens[4].text) }); tokens = tokens.slice(5); } return ret; } var type = item.tokens[1].text; Types.needAnalysis[type] = 0; return { intertype: 'switch', type: type, ident: toNiceIdent(item.tokens[2].text), defaultLabel: toNiceIdent(item.tokens[5].text), switchLabels: parseSwitchLabels(item.tokens[6]), lineNum: item.lineNum }; } // function end function funcEndHandler(item) { return { intertype: 'functionEnd', lineNum: item.lineNum }; } // external function stub function externalHandler(item) { while (item.tokens[1].text in LLVM.LINKAGES || item.tokens[1].text in LLVM.PARAM_ATTR || item.tokens[1].text in LLVM.VISIBILITIES || item.tokens[1].text in LLVM.CALLING_CONVENTIONS) { item.tokens.splice(1, 1); } var params = parseParamTokens(item.tokens[3].tokens); return { intertype: 'functionStub', ident: toNiceIdent(item.tokens[2].text), returnType: item.tokens[1], params: params, hasVarArgs: hasVarArgs(params), lineNum: item.lineNum }; } // 'unreachable' function unreachableHandler(item) { return { intertype: 'unreachable', lineNum: item.lineNum }; } // 'indirectbr' function indirectBrHandler(item) { var ret = { intertype: 'indirectbr', value: parseLLVMSegment(splitTokenList(item.tokens.slice(1))[0]), type: item.tokens[1].text, lineNum: item.lineNum }; Types.needAnalysis[ret.type] = 0; return ret; } // Fast paths - quick parses of common patterns, avoid tokenizing entirely function tryFastPaths(line) { var m, ret; if (phase === 'pre') { // string constant if (0) { // works, but not worth it m = /([@\.\w\d_]+) = (private )?(unnamed_addr )?(constant )?(\[\d+ x i8\]) c"([^"]+)".*/.exec(line.lineText)) { if (m[1] === '@llvm.global_ctors') return ret; ret = { intertype: 'globalVariable', ident: toNiceIdent(m[1]), type: m[5], external: false, private_: m[2] !== null, named: m[3] === null, lineNum: line.lineNum, value: { intertype: 'string', text: m[6] } }; noteGlobalVariable(ret); } } else if (phase === 'funcs') { // TODO: (void)call, store if (m = /^ (%[\w\d\._]+) = (getelementptr|load|icmp) ([%\w\d\._ ,\*\-@]+)$/.exec(line.lineText)) { var assignTo = m[1]; var intertype = m[2]; var args = m[3]; switch (intertype) { case 'getelementptr': { if (args[0] === 'i' && args.indexOf('inbounds ') === 0) { args = args.substr(9); } var params = args.split(', ').map(function(param) { var parts = param.split(' '); assert(parts.length === 2); Types.needAnalysis[parts[0]] = 0; return { intertype: 'value', type: parts[0], ident: toNiceIdent(parts[1]), byVal: 0 } }); ret = { intertype: 'getelementptr', lineNum: line.lineNum, assignTo: toNiceIdent(assignTo), ident: params[0].ident, type: '*', params: params }; break; } case 'load': { if (m = /(^[%\w\d\._\-@\*]+) ([%\w\d\._\-@]+)(, align \d+)?$/.exec(args)) { var ident = toNiceIdent(m[2]); var type = m[1]; assert(type[type.length-1] === '*', type); var valueType = type.substr(0, type.length-1); ret = { intertype: 'load', lineNum: line.lineNum, assignTo: toNiceIdent(assignTo), ident: ident, type: valueType, valueType: valueType, pointerType: type, pointer: { intertype: 'value', ident: ident, type: type, }, align: parseAlign(m[3]) }; } break; } case 'icmp': { var parts = args.split(' '); assert(parts.length === 4); ret = { intertype: 'mathop', op: 'icmp', variant: parts[0], lineNum: line.lineNum, assignTo: toNiceIdent(assignTo), params: [{ intertype: 'value', ident: toNiceIdent(parts[2].substr(0, parts[2].length-1)), type: parts[1] }, { intertype: 'value', ident: toNiceIdent(parts[3]), type: parts[1] }], type: 'i1', }; break; } default: throw 'unexpected fast path type ' + intertype; } } //else if (line.lineText.indexOf(' = icmp ') > 0) printErr('close: ' + JSON.stringify(line.lineText)); } if (ret) { if (COMPILER_ASSERTIONS) { //printErr(['\n', dump(ret), '\n', dump(triager(tokenizer(line)))]); var normal = triager(tokenize(line)); delete normal.tokens; delete normal.indent; assert(sortedJsonCompare(normal, ret), 'fast path: ' + dump(normal) + '\n vs \n' + dump(ret)); } } return ret; } // Input lineSplitter().forEach(function(line) { var item; if (COMPILER_FASTPATHS) { item = tryFastPaths(line); if (item) { finalResults.push(item); fastPaths++; return; } } slowPaths++; //var time = Date.now(); // parse out the assignment var indent = 0, assignTo = null; if (phase === 'pre') { var m = /^([%@\w\d\._\-]+|[%@]"[^"]+") = (.*)/.exec(line.lineText); if (m) { assignTo = m[1]; line.lineText = m[2]; } } else if (phase === 'funcs') { var m = /^ ([%@\w\d\._\-]+|[%@]"[^"]+") = (.*)/.exec(line.lineText); if (m) { indent = 2; assignTo = m[1]; line.lineText = m[2]; } } var tokens = tokenize(line.lineText); var t = { tokens: tokens, indent: indent || line.lineText.search(/[^ ]/), lineNum: line.lineNum }; if (assignTo) { t.assignTo = t.tokens[0].text !== 'type' ? toNiceIdent(assignTo) : assignTo; } item = triager(t); /* var type = item ? item.intertype + (item.op ? ':' + item.op : ''): 'none'; if (!interProf[type]) interProf[type] = { ms: 0, n: 0 }; interProf[type].ms += Date.now() - time; interProf[type].n++; */ if (!item) return; finalResults.push(item); if (item.tokens) item.tokens = null; // We do not need tokens, past the intertyper. Clean them up as soon as possible here. }); //B.stop('intertyper'); return finalResults; } // intertyper profiling /* var interProf = {}; function dumpInterProf() { printErr('\nintertyper/' + phase + ' (ms | n): ' + JSON.stringify(keys(interProf).sort(function(x, y) { return interProf[y].ms - interProf[x].ms }).map(function(x) { return x + ' : ' + interProf[x].ms + ' | ' + interProf[x].n }), null, ' ') + '\n'); } */ //var hits = 0;