| author | |
| committer | |
| log | ab3ac291ac08435f7aba1fc8a53fe0a0290cc1e1 |
| tree | 5f6dd2a4a7dc97a72e3652fff09399ed1cba8b83 |
| parent | 37cdb5dbf90acd61584bae4a6661d0a6f9b54295 |
| parent | c97d64b677eb891144fb356e1f4b9011c60cc0e2 |
51 files changed, 2502 insertions(+), 1721 deletions(-)
lib/docs/main.js+231-64| ... | ... | @@ -203,7 +203,7 @@ var zigAnalysis; |
| 203 | 203 | if (!("type" in resolvedExpr)) { |
| 204 | 204 | return null; |
| 205 | 205 | } |
| 206 | let type = zigAnalysis.types[resolvedExpr.type]; | |
| 206 | let type = getType(resolvedExpr.type); | |
| 207 | 207 | |
| 208 | 208 | outer: for (let i = 0; i < 10000; i += 1) { |
| 209 | 209 | switch (type.kind) { |
| ... | ... | @@ -212,7 +212,7 @@ var zigAnalysis; |
| 212 | 212 | let child = type.child; |
| 213 | 213 | let resolvedChild = resolveValue(child); |
| 214 | 214 | if ("type" in resolvedChild) { |
| 215 | type = zigAnalysis.types[resolvedChild.type]; | |
| 215 | type = getType(resolvedChild.type); | |
| 216 | 216 | continue; |
| 217 | 217 | } else { |
| 218 | 218 | return null; |
| ... | ... | @@ -276,7 +276,7 @@ var zigAnalysis; |
| 276 | 276 | } |
| 277 | 277 | |
| 278 | 278 | if ("declRef" in value.expr) { |
| 279 | value = zigAnalysis.decls[value.expr.declRef].value; | |
| 279 | value = getDecl(value.expr.declRef).value; | |
| 280 | 280 | continue; |
| 281 | 281 | } |
| 282 | 282 | |
| ... | ... | @@ -430,7 +430,7 @@ var zigAnalysis; |
| 430 | 430 | curNav.pkgObjs.push(pkg); |
| 431 | 431 | } |
| 432 | 432 | |
| 433 | let currentType = zigAnalysis.types[pkg.main]; | |
| 433 | let currentType = getType(pkg.main); | |
| 434 | 434 | curNav.declObjs = [currentType]; |
| 435 | 435 | for (let i = 0; i < curNav.declNames.length; i += 1) { |
| 436 | 436 | let childDecl = findSubDecl(currentType, curNav.declNames[i]); |
| ... | ... | @@ -440,7 +440,7 @@ var zigAnalysis; |
| 440 | 440 | |
| 441 | 441 | let childDeclValue = resolveValue(childDecl.value).expr; |
| 442 | 442 | if ("type" in childDeclValue) { |
| 443 | const t = zigAnalysis.types[childDeclValue.type]; | |
| 443 | const t = getType(childDeclValue.type); | |
| 444 | 444 | if (t.kind != typeKinds.Fn) { |
| 445 | 445 | childDecl = t; |
| 446 | 446 | } |
| ... | ... | @@ -478,19 +478,21 @@ var zigAnalysis; |
| 478 | 478 | } |
| 479 | 479 | |
| 480 | 480 | if (lastIsDecl && last.kind === "const") { |
| 481 | let typeObj = zigAnalysis.types[resolveValue(last.value).expr.type]; | |
| 482 | if (typeObj && typeObj.kind === typeKinds.Fn) { | |
| 483 | return renderFn(last); | |
| 481 | const value = resolveValue(last.value); | |
| 482 | if ("type" in value.expr) { | |
| 483 | let typeObj = getType(value.expr.type); | |
| 484 | if (typeObj.kind === typeKinds.Fn) { | |
| 485 | return renderFn(last); | |
| 486 | } | |
| 484 | 487 | } |
| 485 | ||
| 486 | 488 | return renderValue(last); |
| 487 | 489 | } |
| 488 | 490 | |
| 489 | 491 | } |
| 490 | 492 | |
| 491 | 493 | function renderDocTest(decl) { |
| 492 | if (!("decltest" in decl)) return; | |
| 493 | const astNode = zigAnalysis.astNodes[decl.decltest]; | |
| 494 | if (!decl.decltest) return; | |
| 495 | const astNode = getAstNode(decl.decltest); | |
| 494 | 496 | domSectDocTests.classList.remove("hidden"); |
| 495 | 497 | domDocTestsCode.innerHTML = astNode.code; |
| 496 | 498 | } |
| ... | ... | @@ -498,7 +500,7 @@ var zigAnalysis; |
| 498 | 500 | function renderUnknownDecl(decl) { |
| 499 | 501 | domDeclNoRef.classList.remove("hidden"); |
| 500 | 502 | |
| 501 | let docs = zigAnalysis.astNodes[decl.src].docs; | |
| 503 | let docs = getAstNode(decl.src).docs; | |
| 502 | 504 | if (docs != null) { |
| 503 | 505 | domTldDocs.innerHTML = markdown(docs); |
| 504 | 506 | } else { |
| ... | ... | @@ -509,18 +511,18 @@ var zigAnalysis; |
| 509 | 511 | } |
| 510 | 512 | |
| 511 | 513 | function typeIsErrSet(typeIndex) { |
| 512 | let typeObj = zigAnalysis.types[typeIndex]; | |
| 514 | let typeObj = getType(typeIndex); | |
| 513 | 515 | return typeObj.kind === typeKinds.ErrorSet; |
| 514 | 516 | } |
| 515 | 517 | |
| 516 | 518 | function typeIsStructWithNoFields(typeIndex) { |
| 517 | let typeObj = zigAnalysis.types[typeIndex]; | |
| 519 | let typeObj = getType(typeIndex); | |
| 518 | 520 | if (typeObj.kind !== typeKinds.Struct) return false; |
| 519 | 521 | return typeObj.fields.length == 0; |
| 520 | 522 | } |
| 521 | 523 | |
| 522 | 524 | function typeIsGenericFn(typeIndex) { |
| 523 | let typeObj = zigAnalysis.types[typeIndex]; | |
| 525 | let typeObj = getType(typeIndex); | |
| 524 | 526 | if (typeObj.kind !== typeKinds.Fn) { |
| 525 | 527 | return false; |
| 526 | 528 | } |
| ... | ... | @@ -532,12 +534,12 @@ var zigAnalysis; |
| 532 | 534 | let last = fnDecl.value.expr.refPath.length - 1; |
| 533 | 535 | let lastExpr = fnDecl.value.expr.refPath[last]; |
| 534 | 536 | console.assert("declRef" in lastExpr); |
| 535 | fnDecl = zigAnalysis.decls[lastExpr.declRef]; | |
| 537 | fnDecl = getDecl(lastExpr.declRef); | |
| 536 | 538 | } |
| 537 | 539 | |
| 538 | 540 | let value = resolveValue(fnDecl.value); |
| 539 | 541 | console.assert("type" in value.expr); |
| 540 | let typeObj = zigAnalysis.types[value.expr.type]; | |
| 542 | let typeObj = getType(value.expr.type); | |
| 541 | 543 | |
| 542 | 544 | domFnProtoCode.innerHTML = exprName(value.expr, { |
| 543 | 545 | wantHtml: true, |
| ... | ... | @@ -546,7 +548,7 @@ var zigAnalysis; |
| 546 | 548 | }); |
| 547 | 549 | |
| 548 | 550 | let docsSource = null; |
| 549 | let srcNode = zigAnalysis.astNodes[fnDecl.src]; | |
| 551 | let srcNode = getAstNode(fnDecl.src); | |
| 550 | 552 | if (srcNode.docs != null) { |
| 551 | 553 | docsSource = srcNode.docs; |
| 552 | 554 | } |
| ... | ... | @@ -557,14 +559,14 @@ var zigAnalysis; |
| 557 | 559 | if ("type" in retExpr) { |
| 558 | 560 | let retIndex = retExpr.type; |
| 559 | 561 | let errSetTypeIndex = null; |
| 560 | let retType = zigAnalysis.types[retIndex]; | |
| 562 | let retType = getType(retIndex); | |
| 561 | 563 | if (retType.kind === typeKinds.ErrorSet) { |
| 562 | 564 | errSetTypeIndex = retIndex; |
| 563 | 565 | } else if (retType.kind === typeKinds.ErrorUnion) { |
| 564 | 566 | errSetTypeIndex = retType.err.type; |
| 565 | 567 | } |
| 566 | 568 | if (errSetTypeIndex != null) { |
| 567 | let errSetType = zigAnalysis.types[errSetTypeIndex]; | |
| 569 | let errSetType = getType(errSetTypeIndex); | |
| 568 | 570 | renderErrorSet(errSetType); |
| 569 | 571 | } |
| 570 | 572 | } |
| ... | ... | @@ -578,7 +580,7 @@ var zigAnalysis; |
| 578 | 580 | let call = zigAnalysis.calls[resolvedGenericRet.expr.call]; |
| 579 | 581 | let resolvedFunc = resolveValue({ expr: call.func }); |
| 580 | 582 | if (!("type" in resolvedFunc.expr)) return; |
| 581 | let callee = zigAnalysis.types[resolvedFunc.expr.type]; | |
| 583 | let callee = getType(resolvedFunc.expr.type); | |
| 582 | 584 | if (!callee.generic_ret) return; |
| 583 | 585 | resolvedGenericRet = resolveValue({ expr: callee.generic_ret }); |
| 584 | 586 | } |
| ... | ... | @@ -591,7 +593,7 @@ var zigAnalysis; |
| 591 | 593 | } |
| 592 | 594 | |
| 593 | 595 | if (!("type" in resolvedGenericRet.expr)) return; |
| 594 | const genericType = zigAnalysis.types[resolvedGenericRet.expr.type]; | |
| 596 | const genericType = getType(resolvedGenericRet.expr.type); | |
| 595 | 597 | if (isContainerType(genericType)) { |
| 596 | 598 | renderContainer(genericType); |
| 597 | 599 | } |
| ... | ... | @@ -621,7 +623,7 @@ var zigAnalysis; |
| 621 | 623 | domFnNoExamples.classList.add("hidden"); |
| 622 | 624 | } |
| 623 | 625 | |
| 624 | let protoSrcNode = zigAnalysis.astNodes[protoSrcIndex]; | |
| 626 | let protoSrcNode = getAstNode(protoSrcIndex); | |
| 625 | 627 | if ( |
| 626 | 628 | docsSource == null && |
| 627 | 629 | protoSrcNode != null && |
| ... | ... | @@ -639,13 +641,13 @@ var zigAnalysis; |
| 639 | 641 | function renderFnParamDocs(fnDecl, typeObj) { |
| 640 | 642 | let docCount = 0; |
| 641 | 643 | |
| 642 | let fnNode = zigAnalysis.astNodes[fnDecl.src]; | |
| 644 | let fnNode = getAstNode(fnDecl.src); | |
| 643 | 645 | let fields = fnNode.fields; |
| 644 | 646 | let isVarArgs = fnNode.varArgs; |
| 645 | 647 | |
| 646 | 648 | for (let i = 0; i < fields.length; i += 1) { |
| 647 | 649 | let field = fields[i]; |
| 648 | let fieldNode = zigAnalysis.astNodes[field]; | |
| 650 | let fieldNode = getAstNode(field); | |
| 649 | 651 | if (fieldNode.docs != null) { |
| 650 | 652 | docCount += 1; |
| 651 | 653 | } |
| ... | ... | @@ -659,7 +661,7 @@ var zigAnalysis; |
| 659 | 661 | |
| 660 | 662 | for (let i = 0; i < fields.length; i += 1) { |
| 661 | 663 | let field = fields[i]; |
| 662 | let fieldNode = zigAnalysis.astNodes[field]; | |
| 664 | let fieldNode = getAstNode(field); | |
| 663 | 665 | let docs = fieldNode.docs; |
| 664 | 666 | if (fieldNode.docs == null) { |
| 665 | 667 | continue; |
| ... | ... | @@ -967,17 +969,17 @@ var zigAnalysis; |
| 967 | 969 | } |
| 968 | 970 | case "switchOp": { |
| 969 | 971 | let condExpr = zigAnalysis.exprs[expr.switchOp.cond_index]; |
| 970 | let ast = zigAnalysis.astNodes[expr.switchOp.ast]; | |
| 972 | let ast = getAstNode(expr.switchOp.src); | |
| 971 | 973 | let file_name = expr.switchOp.file_name; |
| 972 | 974 | let outer_decl_index = expr.switchOp.outer_decl; |
| 973 | let outer_decl = zigAnalysis.types[outer_decl_index]; | |
| 975 | let outer_decl = getType(outer_decl_index); | |
| 974 | 976 | let line = 0; |
| 975 | 977 | // console.log(expr.switchOp) |
| 976 | 978 | // console.log(outer_decl) |
| 977 | 979 | while (outer_decl_index !== 0 && outer_decl.line_number > 0) { |
| 978 | 980 | line += outer_decl.line_number; |
| 979 | 981 | outer_decl_index = outer_decl.outer_decl; |
| 980 | outer_decl = zigAnalysis.types[outer_decl_index]; | |
| 982 | outer_decl = getType(outer_decl_index); | |
| 981 | 983 | // console.log(outer_decl) |
| 982 | 984 | } |
| 983 | 985 | line += ast.line + 1; |
| ... | ... | @@ -1028,8 +1030,8 @@ var zigAnalysis; |
| 1028 | 1030 | case "fieldRef": { |
| 1029 | 1031 | const enumObj = exprName({ type: expr.fieldRef.type }, opts); |
| 1030 | 1032 | const field = |
| 1031 | zigAnalysis.astNodes[enumObj.ast].fields[expr.fieldRef.index]; | |
| 1032 | const name = zigAnalysis.astNodes[field].name; | |
| 1033 | getAstNode(enumObj.src).fields[expr.fieldRef.index]; | |
| 1034 | const name = getAstNode(field).name; | |
| 1033 | 1035 | return name; |
| 1034 | 1036 | } |
| 1035 | 1037 | case "enumToInt": { |
| ... | ... | @@ -1452,13 +1454,13 @@ var zigAnalysis; |
| 1452 | 1454 | return print_lhs + " " + operator + " " + print_rhs; |
| 1453 | 1455 | } |
| 1454 | 1456 | case "errorSets": { |
| 1455 | const errUnionObj = zigAnalysis.types[expr.errorSets]; | |
| 1457 | const errUnionObj = getType(expr.errorSets); | |
| 1456 | 1458 | let lhs = exprName(errUnionObj.lhs, opts); |
| 1457 | 1459 | let rhs = exprName(errUnionObj.rhs, opts); |
| 1458 | 1460 | return lhs + " || " + rhs; |
| 1459 | 1461 | } |
| 1460 | 1462 | case "errorUnion": { |
| 1461 | const errUnionObj = zigAnalysis.types[expr.errorUnion]; | |
| 1463 | const errUnionObj = getType(expr.errorUnion); | |
| 1462 | 1464 | let lhs = exprName(errUnionObj.lhs, opts); |
| 1463 | 1465 | let rhs = exprName(errUnionObj.rhs, opts); |
| 1464 | 1466 | return lhs + "!" + rhs; |
| ... | ... | @@ -1574,7 +1576,7 @@ var zigAnalysis; |
| 1574 | 1576 | return exprName(exprArg, opts); |
| 1575 | 1577 | } |
| 1576 | 1578 | case "declRef": { |
| 1577 | return zigAnalysis.decls[expr.declRef].name; | |
| 1579 | return getDecl(expr.declRef).name; | |
| 1578 | 1580 | } |
| 1579 | 1581 | case "refPath": { |
| 1580 | 1582 | return expr.refPath.map((x) => exprName(x, opts)).join("."); |
| ... | ... | @@ -1611,7 +1613,7 @@ var zigAnalysis; |
| 1611 | 1613 | let name = ""; |
| 1612 | 1614 | |
| 1613 | 1615 | let typeObj = expr.type; |
| 1614 | if (typeof typeObj === "number") typeObj = zigAnalysis.types[typeObj]; | |
| 1616 | if (typeof typeObj === "number") typeObj = getType(typeObj); | |
| 1615 | 1617 | switch (typeObj.kind) { |
| 1616 | 1618 | default: |
| 1617 | 1619 | throw "TODO"; |
| ... | ... | @@ -1865,7 +1867,7 @@ var zigAnalysis; |
| 1865 | 1867 | if (fnObj.params) { |
| 1866 | 1868 | let fields = null; |
| 1867 | 1869 | let isVarArgs = false; |
| 1868 | let fnNode = zigAnalysis.astNodes[fnObj.src]; | |
| 1870 | let fnNode = getAstNode(fnObj.src); | |
| 1869 | 1871 | fields = fnNode.fields; |
| 1870 | 1872 | isVarArgs = fnNode.varArgs; |
| 1871 | 1873 | |
| ... | ... | @@ -1880,7 +1882,7 @@ var zigAnalysis; |
| 1880 | 1882 | let paramValue = resolveValue({ expr: value }); |
| 1881 | 1883 | |
| 1882 | 1884 | if (fields != null) { |
| 1883 | let paramNode = zigAnalysis.astNodes[fields[i]]; | |
| 1885 | let paramNode = getAstNode(fields[i]); | |
| 1884 | 1886 | |
| 1885 | 1887 | if (paramNode.varArgs) { |
| 1886 | 1888 | payloadHtml += "..."; |
| ... | ... | @@ -2046,7 +2048,7 @@ var zigAnalysis; |
| 2046 | 2048 | function shouldSkipParamName(typeRef, paramName) { |
| 2047 | 2049 | let resolvedTypeRef = resolveValue({ expr: typeRef }); |
| 2048 | 2050 | if ("type" in resolvedTypeRef) { |
| 2049 | let typeObj = zigAnalysis.types[resolvedTypeRef.type]; | |
| 2051 | let typeObj = getType(resolvedTypeRef.type); | |
| 2050 | 2052 | if (typeObj.kind === typeKinds.Pointer) { |
| 2051 | 2053 | let ptrObj = typeObj; |
| 2052 | 2054 | if (getPtrSize(ptrObj) === pointerSizeEnum.One) { |
| ... | ... | @@ -2067,7 +2069,7 @@ var zigAnalysis; |
| 2067 | 2069 | if ( |
| 2068 | 2070 | rootIsStd && |
| 2069 | 2071 | typeObj === |
| 2070 | zigAnalysis.types[zigAnalysis.packages[zigAnalysis.rootPkg].main] | |
| 2072 | getType(zigAnalysis.packages[zigAnalysis.rootPkg].main) | |
| 2071 | 2073 | ) { |
| 2072 | 2074 | name = "std"; |
| 2073 | 2075 | } else { |
| ... | ... | @@ -2189,7 +2191,7 @@ var zigAnalysis; |
| 2189 | 2191 | |
| 2190 | 2192 | if (resolvedValue.expr.fieldRef) { |
| 2191 | 2193 | const declRef = decl.value.expr.refPath[0].declRef; |
| 2192 | const type = zigAnalysis.decls[declRef]; | |
| 2194 | const type = getDecl(declRef); | |
| 2193 | 2195 | domFnProtoCode.innerHTML = |
| 2194 | 2196 | '<span class="tok-kw">const</span> ' + |
| 2195 | 2197 | escapeHtml(decl.name) + |
| ... | ... | @@ -2229,7 +2231,7 @@ var zigAnalysis; |
| 2229 | 2231 | ";"; |
| 2230 | 2232 | } |
| 2231 | 2233 | |
| 2232 | let docs = zigAnalysis.astNodes[decl.src].docs; | |
| 2234 | let docs = getAstNode(decl.src).docs; | |
| 2233 | 2235 | if (docs != null) { |
| 2234 | 2236 | domTldDocs.innerHTML = markdown(docs); |
| 2235 | 2237 | domTldDocs.classList.remove("hidden"); |
| ... | ... | @@ -2246,7 +2248,7 @@ var zigAnalysis; |
| 2246 | 2248 | ": " + |
| 2247 | 2249 | typeValueName(declTypeRef, true, true); |
| 2248 | 2250 | |
| 2249 | let docs = zigAnalysis.astNodes[decl.src].docs; | |
| 2251 | let docs = getAstNode(decl.src).docs; | |
| 2250 | 2252 | if (docs != null) { |
| 2251 | 2253 | domTldDocs.innerHTML = markdown(docs); |
| 2252 | 2254 | domTldDocs.classList.remove("hidden"); |
| ... | ... | @@ -2266,7 +2268,7 @@ var zigAnalysis; |
| 2266 | 2268 | testsList |
| 2267 | 2269 | ) { |
| 2268 | 2270 | for (let i = 0; i < decls.length; i += 1) { |
| 2269 | let decl = zigAnalysis.decls[decls[i]]; | |
| 2271 | let decl = getDecl(decls[i]); | |
| 2270 | 2272 | let declValue = resolveValue(decl.value); |
| 2271 | 2273 | |
| 2272 | 2274 | if (decl.isTest) { |
| ... | ... | @@ -2282,7 +2284,7 @@ var zigAnalysis; |
| 2282 | 2284 | if (decl.kind === "const") { |
| 2283 | 2285 | if ("type" in declValue.expr) { |
| 2284 | 2286 | // We have the actual type expression at hand. |
| 2285 | const typeExpr = zigAnalysis.types[declValue.expr.type]; | |
| 2287 | const typeExpr = getType(declValue.expr.type); | |
| 2286 | 2288 | if (typeExpr.kind == typeKinds.Fn) { |
| 2287 | 2289 | const funcRetExpr = resolveValue({ |
| 2288 | 2290 | expr: typeExpr.ret, |
| ... | ... | @@ -2310,7 +2312,7 @@ var zigAnalysis; |
| 2310 | 2312 | typesList.push(decl); |
| 2311 | 2313 | } |
| 2312 | 2314 | } |
| 2313 | } else if ("typeRef" in declValue) { | |
| 2315 | } else if (declValue.typeRef) { | |
| 2314 | 2316 | if ("type" in declValue.typeRef && declValue.typeRef == typeTypeId) { |
| 2315 | 2317 | // We don't know what the type expression is, but we know it's a type. |
| 2316 | 2318 | typesList.push(decl); |
| ... | ... | @@ -2324,7 +2326,7 @@ var zigAnalysis; |
| 2324 | 2326 | } |
| 2325 | 2327 | } |
| 2326 | 2328 | function renderSourceFileLink(decl) { |
| 2327 | let srcNode = zigAnalysis.astNodes[decl.src]; | |
| 2329 | let srcNode = getAstNode(decl.src); | |
| 2328 | 2330 | |
| 2329 | 2331 | return "<a style=\"float: right;\" href=\"" + |
| 2330 | 2332 | sourceFileUrlTemplate.replace("{{file}}", |
| ... | ... | @@ -2377,7 +2379,7 @@ var zigAnalysis; |
| 2377 | 2379 | testsList.sort(byNameProperty); |
| 2378 | 2380 | |
| 2379 | 2381 | if (container.src != null) { |
| 2380 | let docs = zigAnalysis.astNodes[container.src].docs; | |
| 2382 | let docs = getAstNode(container.src).docs; | |
| 2381 | 2383 | if (docs != null) { |
| 2382 | 2384 | domTldDocs.innerHTML = markdown(docs); |
| 2383 | 2385 | domTldDocs.classList.remove("hidden"); |
| ... | ... | @@ -2457,7 +2459,7 @@ var zigAnalysis; |
| 2457 | 2459 | }); |
| 2458 | 2460 | tdFnSrc.innerHTML = renderSourceFileLink(decl); |
| 2459 | 2461 | |
| 2460 | let docs = zigAnalysis.astNodes[decl.src].docs; | |
| 2462 | let docs = getAstNode(decl.src).docs; | |
| 2461 | 2463 | if (docs != null) { |
| 2462 | 2464 | tdDesc.innerHTML = shortDescMarkdown(docs); |
| 2463 | 2465 | } else { |
| ... | ... | @@ -2467,12 +2469,12 @@ var zigAnalysis; |
| 2467 | 2469 | domSectFns.classList.remove("hidden"); |
| 2468 | 2470 | } |
| 2469 | 2471 | |
| 2470 | let containerNode = zigAnalysis.astNodes[container.src]; | |
| 2472 | let containerNode = getAstNode(container.src); | |
| 2471 | 2473 | if (containerNode.fields && containerNode.fields.length > 0) { |
| 2472 | 2474 | resizeDomList(domListFields, containerNode.fields.length, "<div></div>"); |
| 2473 | 2475 | |
| 2474 | 2476 | for (let i = 0; i < containerNode.fields.length; i += 1) { |
| 2475 | let fieldNode = zigAnalysis.astNodes[containerNode.fields[i]]; | |
| 2477 | let fieldNode = getAstNode(containerNode.fields[i]); | |
| 2476 | 2478 | let divDom = domListFields.children[i]; |
| 2477 | 2479 | let fieldName = fieldNode.name; |
| 2478 | 2480 | let docs = fieldNode.docs; |
| ... | ... | @@ -2528,7 +2530,7 @@ var zigAnalysis; |
| 2528 | 2530 | |
| 2529 | 2531 | tdType.innerHTML = typeValueName(typeOfDecl(decl), true, true); |
| 2530 | 2532 | |
| 2531 | let docs = zigAnalysis.astNodes[decl.src].docs; | |
| 2533 | let docs = getAstNode(decl.src).docs; | |
| 2532 | 2534 | if (docs != null) { |
| 2533 | 2535 | tdDesc.innerHTML = shortDescMarkdown(docs); |
| 2534 | 2536 | } else { |
| ... | ... | @@ -2561,7 +2563,7 @@ var zigAnalysis; |
| 2561 | 2563 | wantLink: true, |
| 2562 | 2564 | }); |
| 2563 | 2565 | |
| 2564 | let docs = zigAnalysis.astNodes[decl.src].docs; | |
| 2566 | let docs = getAstNode(decl.src).docs; | |
| 2565 | 2567 | if (docs != null) { |
| 2566 | 2568 | tdDesc.innerHTML = shortDescMarkdown(docs); |
| 2567 | 2569 | } else { |
| ... | ... | @@ -2594,7 +2596,7 @@ var zigAnalysis; |
| 2594 | 2596 | wantLink: true, |
| 2595 | 2597 | }); |
| 2596 | 2598 | |
| 2597 | let docs = zigAnalysis.astNodes[decl.src].docs; | |
| 2599 | let docs = getAstNode(decl.src).docs; | |
| 2598 | 2600 | if (docs != null) { |
| 2599 | 2601 | tdDesc.innerHTML = shortDescMarkdown(docs); |
| 2600 | 2602 | } else { |
| ... | ... | @@ -2668,7 +2670,7 @@ var zigAnalysis; |
| 2668 | 2670 | |
| 2669 | 2671 | function findTypeTypeId() { |
| 2670 | 2672 | for (let i = 0; i < zigAnalysis.types.length; i += 1) { |
| 2671 | if (zigAnalysis.types[i].kind == typeKinds.Type) { | |
| 2673 | if (getType(i).kind == typeKinds.Type) { | |
| 2672 | 2674 | return i; |
| 2673 | 2675 | } |
| 2674 | 2676 | } |
| ... | ... | @@ -2732,11 +2734,11 @@ var zigAnalysis; |
| 2732 | 2734 | if ("value" in parentType) { |
| 2733 | 2735 | const rv = resolveValue(parentType.value); |
| 2734 | 2736 | if ("type" in rv.expr) { |
| 2735 | const t = zigAnalysis.types[rv.expr.type]; | |
| 2737 | const t = getType(rv.expr.type); | |
| 2736 | 2738 | if (t.kind == typeKinds.Fn && t.generic_ret != null) { |
| 2737 | 2739 | const rgr = resolveValue({ expr: t.generic_ret }); |
| 2738 | 2740 | if ("type" in rgr.expr) { |
| 2739 | parentType = zigAnalysis.types[rgr.expr.type]; | |
| 2741 | parentType = getType(rgr.expr.type); | |
| 2740 | 2742 | } |
| 2741 | 2743 | } |
| 2742 | 2744 | } |
| ... | ... | @@ -2746,7 +2748,7 @@ var zigAnalysis; |
| 2746 | 2748 | if (!parentType.pubDecls) return null; |
| 2747 | 2749 | for (let i = 0; i < parentType.pubDecls.length; i += 1) { |
| 2748 | 2750 | let declIndex = parentType.pubDecls[i]; |
| 2749 | let childDecl = zigAnalysis.decls[declIndex]; | |
| 2751 | let childDecl = getDecl(declIndex); | |
| 2750 | 2752 | if (childDecl.name === childName) { |
| 2751 | 2753 | return childDecl; |
| 2752 | 2754 | } |
| ... | ... | @@ -2754,7 +2756,7 @@ var zigAnalysis; |
| 2754 | 2756 | if (!parentType.privDecls) return null; |
| 2755 | 2757 | for (let i = 0; i < parentType.privDecls.length; i += 1) { |
| 2756 | 2758 | let declIndex = parentType.privDecls[i]; |
| 2757 | let childDecl = zigAnalysis.decls[declIndex]; | |
| 2759 | let childDecl = getDecl(declIndex); | |
| 2758 | 2760 | if (childDecl.name === childName) { |
| 2759 | 2761 | return childDecl; |
| 2760 | 2762 | } |
| ... | ... | @@ -2805,7 +2807,7 @@ var zigAnalysis; |
| 2805 | 2807 | let stack = [ |
| 2806 | 2808 | { |
| 2807 | 2809 | declNames: [], |
| 2808 | type: zigAnalysis.types[pkg.main], | |
| 2810 | type: getType(pkg.main), | |
| 2809 | 2811 | }, |
| 2810 | 2812 | ]; |
| 2811 | 2813 | while (stack.length !== 0) { |
| ... | ... | @@ -2819,7 +2821,7 @@ var zigAnalysis; |
| 2819 | 2821 | let mainDeclIndex = t.pubDecls[declI]; |
| 2820 | 2822 | if (list[mainDeclIndex] != null) continue; |
| 2821 | 2823 | |
| 2822 | let decl = zigAnalysis.decls[mainDeclIndex]; | |
| 2824 | let decl = getDecl(mainDeclIndex); | |
| 2823 | 2825 | let declVal = resolveValue(decl.value); |
| 2824 | 2826 | let declNames = item.declNames.concat([decl.name]); |
| 2825 | 2827 | list[mainDeclIndex] = { |
| ... | ... | @@ -2827,7 +2829,7 @@ var zigAnalysis; |
| 2827 | 2829 | declNames: declNames, |
| 2828 | 2830 | }; |
| 2829 | 2831 | if ("type" in declVal.expr) { |
| 2830 | let value = zigAnalysis.types[declVal.expr.type]; | |
| 2832 | let value = getType(declVal.expr.type); | |
| 2831 | 2833 | if (declCanRepresentTypeKind(value.kind)) { |
| 2832 | 2834 | canonTypeDecls[declVal.type] = mainDeclIndex; |
| 2833 | 2835 | } |
| ... | ... | @@ -2843,7 +2845,7 @@ var zigAnalysis; |
| 2843 | 2845 | if (value.kind == typeKinds.Fn && value.generic_ret != null) { |
| 2844 | 2846 | let resolvedVal = resolveValue({ expr: value.generic_ret }); |
| 2845 | 2847 | if ("type" in resolvedVal.expr) { |
| 2846 | let generic_type = zigAnalysis.types[resolvedVal.expr.type]; | |
| 2848 | let generic_type = getType(resolvedVal.expr.type); | |
| 2847 | 2849 | if (isContainerType(generic_type)) { |
| 2848 | 2850 | stack.push({ |
| 2849 | 2851 | declNames: declNames, |
| ... | ... | @@ -3394,11 +3396,11 @@ var zigAnalysis; |
| 3394 | 3396 | let canonPath = getCanonDeclPath(declIndex); |
| 3395 | 3397 | if (canonPath == null) continue; |
| 3396 | 3398 | |
| 3397 | let decl = zigAnalysis.decls[declIndex]; | |
| 3399 | let decl = getDecl(declIndex); | |
| 3398 | 3400 | let lastPkgName = canonPath.pkgNames[canonPath.pkgNames.length - 1]; |
| 3399 | 3401 | let fullPathSearchText = |
| 3400 | 3402 | lastPkgName + "." + canonPath.declNames.join("."); |
| 3401 | let astNode = zigAnalysis.astNodes[decl.src]; | |
| 3403 | let astNode = getAstNode(decl.src); | |
| 3402 | 3404 | let fileAndDocs = ""; //zigAnalysis.files[astNode.file]; |
| 3403 | 3405 | // TODO: understand what this piece of code is trying to achieve |
| 3404 | 3406 | // also right now `files` are expressed as a hashmap. |
| ... | ... | @@ -3513,4 +3515,169 @@ var zigAnalysis; |
| 3513 | 3515 | function byNameProperty(a, b) { |
| 3514 | 3516 | return operatorCompare(a.name, b.name); |
| 3515 | 3517 | } |
| 3518 | ||
| 3519 | ||
| 3520 | function getDecl(idx) { | |
| 3521 | const decl = zigAnalysis.decls[idx]; | |
| 3522 | return { | |
| 3523 | name: decl[0], | |
| 3524 | kind: decl[1], | |
| 3525 | isTest: decl[2], | |
| 3526 | src: decl[3], | |
| 3527 | value: decl[4], | |
| 3528 | decltest: decl[5], | |
| 3529 | }; | |
| 3530 | } | |
| 3531 | ||
| 3532 | function getAstNode(idx) { | |
| 3533 | const ast = zigAnalysis.astNodes[idx]; | |
| 3534 | return { | |
| 3535 | file: ast[0], | |
| 3536 | line: ast[1], | |
| 3537 | col: ast[2], | |
| 3538 | name: ast[3], | |
| 3539 | code: ast[4], | |
| 3540 | docs: ast[5], | |
| 3541 | fields: ast[6], | |
| 3542 | comptime: ast[7], | |
| 3543 | }; | |
| 3544 | } | |
| 3545 | ||
| 3546 | function getType(idx){ | |
| 3547 | const ty = zigAnalysis.types[idx]; | |
| 3548 | switch(ty[0]) { | |
| 3549 | default: | |
| 3550 | throw "unhandled type kind!"; | |
| 3551 | case 0: // Unanalyzed | |
| 3552 | throw "unanalyzed type!"; | |
| 3553 | case 1: // Type | |
| 3554 | case 2: // Void | |
| 3555 | case 3: // Bool | |
| 3556 | case 4: // NoReturn | |
| 3557 | case 5: // Int | |
| 3558 | case 6: // Float | |
| 3559 | return { kind: ty[0], name: ty[1]}; | |
| 3560 | case 7: // Pointer | |
| 3561 | return { | |
| 3562 | kind: ty[0], | |
| 3563 | size: ty[1], | |
| 3564 | child: ty[2], | |
| 3565 | sentinel: ty[3], | |
| 3566 | align: ty[4], | |
| 3567 | address_space: ty[5], | |
| 3568 | bit_start: ty[6], | |
| 3569 | host_size: ty[7], | |
| 3570 | is_ref: ty[8], | |
| 3571 | is_allowzero: ty[9], | |
| 3572 | is_mutable: ty[10], | |
| 3573 | is_volatile: ty[11], | |
| 3574 | has_sentinel: ty[12], | |
| 3575 | has_align: ty[13], | |
| 3576 | has_addrspace: ty[14], | |
| 3577 | has_bit_range: ty[15], | |
| 3578 | }; | |
| 3579 | case 8: // Array | |
| 3580 | return { | |
| 3581 | kind: ty[0], | |
| 3582 | len: ty[1], | |
| 3583 | child: ty[2], | |
| 3584 | sentinel: ty[3], | |
| 3585 | }; | |
| 3586 | case 9: // Struct | |
| 3587 | return { | |
| 3588 | kind: ty[0], | |
| 3589 | name: ty[1], | |
| 3590 | src: ty[2], | |
| 3591 | privDecls: ty[3], | |
| 3592 | pubDecls: ty[4], | |
| 3593 | fields: ty[5], | |
| 3594 | line_number: ty[6], | |
| 3595 | outer_decl: ty[7], | |
| 3596 | }; | |
| 3597 | case 10: // ComptimeExpr | |
| 3598 | case 11: // ComptimeFloat | |
| 3599 | case 12: // ComptimeInt | |
| 3600 | case 13: // Undefined | |
| 3601 | case 14: // Null | |
| 3602 | return { kind: ty[0], name: ty[1] }; | |
| 3603 | case 15: // Optional | |
| 3604 | return { | |
| 3605 | kind: ty[0], | |
| 3606 | name: ty[1], | |
| 3607 | child: ty[2], | |
| 3608 | }; | |
| 3609 | case 16: // ErrorUnion | |
| 3610 | return { | |
| 3611 | kind: ty[0], | |
| 3612 | lhs: ty[1], | |
| 3613 | rhs: ty[2], | |
| 3614 | }; | |
| 3615 | case 17: // InferredErrorUnion | |
| 3616 | return { | |
| 3617 | kind: ty[0], | |
| 3618 | payload: ty[1], | |
| 3619 | }; | |
| 3620 | case 18: // ErrorSet | |
| 3621 | return { | |
| 3622 | kind: ty[0], | |
| 3623 | name: ty[1], | |
| 3624 | fields: ty[2], | |
| 3625 | }; | |
| 3626 | case 19: // Enum | |
| 3627 | return { | |
| 3628 | kind: ty[0], | |
| 3629 | name: ty[1], | |
| 3630 | src: ty[2], | |
| 3631 | privDecls: ty[3], | |
| 3632 | pubDecls: ty[4], | |
| 3633 | }; | |
| 3634 | case 20: // Union | |
| 3635 | return { | |
| 3636 | kind: ty[0], | |
| 3637 | name: ty[1], | |
| 3638 | src: ty[2], | |
| 3639 | privDecls: ty[3], | |
| 3640 | pubDecls: ty[4], | |
| 3641 | fields: ty[5], | |
| 3642 | }; | |
| 3643 | case 21: // Fn | |
| 3644 | return { | |
| 3645 | kind: ty[0], | |
| 3646 | name: ty[1], | |
| 3647 | src: ty[2], | |
| 3648 | ret: ty[3], | |
| 3649 | generic_ret: ty[4], | |
| 3650 | params: ty[5], | |
| 3651 | lib_name: ty[6], | |
| 3652 | is_var_args: ty[7], | |
| 3653 | is_inferred_error: ty[8], | |
| 3654 | has_lib_name: ty[9], | |
| 3655 | has_cc: ty[10], | |
| 3656 | cc: ty[11], | |
| 3657 | align: ty[12], | |
| 3658 | has_align: ty[13], | |
| 3659 | is_test: ty[14], | |
| 3660 | is_extern: ty[15], | |
| 3661 | }; | |
| 3662 | case 22: // BoundFn | |
| 3663 | return { kind: ty[0], name: ty[1] }; | |
| 3664 | case 23: // Opaque | |
| 3665 | return { | |
| 3666 | kind: ty[0], | |
| 3667 | name: ty[1], | |
| 3668 | src: ty[2], | |
| 3669 | privDecls: ty[3], | |
| 3670 | pubDecls: ty[4], | |
| 3671 | }; | |
| 3672 | case 24: // Frame | |
| 3673 | case 25: // AnyFrame | |
| 3674 | case 26: // Vector | |
| 3675 | case 27: // EnumLiteral | |
| 3676 | return { kind: ty[0], name: ty[1] }; | |
| 3677 | } | |
| 3678 | } | |
| 3679 | ||
| 3516 | 3680 | })(); |
| 3681 | ||
| 3682 | ||
| 3683 |
lib/std/child_process.zig+1-1| ... | ... | @@ -508,7 +508,7 @@ pub const ChildProcess = struct { |
| 508 | 508 | // it, that's the error code returned by the child process. |
| 509 | 509 | _ = std.os.poll(&fd, 0) catch unreachable; |
| 510 | 510 | |
| 511 | // According to eventfd(2) the descriptro is readable if the counter | |
| 511 | // According to eventfd(2) the descriptor is readable if the counter | |
| 512 | 512 | // has a value greater than 0 |
| 513 | 513 | if ((fd[0].revents & std.os.POLL.IN) != 0) { |
| 514 | 514 | const err_int = try readIntFd(err_pipe[0]); |
lib/std/linked_list.zig-2| ... | ... | @@ -2,8 +2,6 @@ const std = @import("std.zig"); |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | const assert = debug.assert; |
| 4 | 4 | const testing = std.testing; |
| 5 | const mem = std.mem; | |
| 6 | const Allocator = mem.Allocator; | |
| 7 | 5 | |
| 8 | 6 | /// A singly-linked list is headed by a single forward pointer. The elements |
| 9 | 7 | /// are singly linked for minimum space and pointer manipulation overhead at |
src/Autodoc.zig+83-19| ... | ... | @@ -280,8 +280,8 @@ pub fn generateZirData(self: *Autodoc) !void { |
| 280 | 280 | try std.json.stringify( |
| 281 | 281 | data, |
| 282 | 282 | .{ |
| 283 | .whitespace = .{ .indent = if (builtin.mode == .Debug) .{ .Space = 4 } else .None }, | |
| 284 | .emit_null_optional_fields = false, | |
| 283 | .whitespace = .{ .indent = .None, .separator = false }, | |
| 284 | .emit_null_optional_fields = true, | |
| 285 | 285 | }, |
| 286 | 286 | out, |
| 287 | 287 | ); |
| ... | ... | @@ -404,6 +404,7 @@ const DocData = struct { |
| 404 | 404 | w: anytype, |
| 405 | 405 | ) !void { |
| 406 | 406 | var jsw = std.json.writeStream(w, 15); |
| 407 | if (opts.whitespace) |ws| jsw.whitespace = ws; | |
| 407 | 408 | try jsw.beginObject(); |
| 408 | 409 | inline for (comptime std.meta.tags(std.meta.FieldEnum(DocData))) |f| { |
| 409 | 410 | const f_name = @tagName(f); |
| ... | ... | @@ -449,6 +450,8 @@ const DocData = struct { |
| 449 | 450 | w: anytype, |
| 450 | 451 | ) !void { |
| 451 | 452 | var jsw = std.json.writeStream(w, 15); |
| 453 | if (opts.whitespace) |ws| jsw.whitespace = ws; | |
| 454 | ||
| 452 | 455 | try jsw.beginObject(); |
| 453 | 456 | inline for (comptime std.meta.tags(std.meta.FieldEnum(DocPackage))) |f| { |
| 454 | 457 | const f_name = @tagName(f); |
| ... | ... | @@ -474,6 +477,22 @@ const DocData = struct { |
| 474 | 477 | // The index in astNodes of the `test declname { }` node |
| 475 | 478 | decltest: ?usize = null, |
| 476 | 479 | _analyzed: bool, // omitted in json data |
| 480 | ||
| 481 | pub fn jsonStringify( | |
| 482 | self: Decl, | |
| 483 | opts: std.json.StringifyOptions, | |
| 484 | w: anytype, | |
| 485 | ) !void { | |
| 486 | var jsw = std.json.writeStream(w, 15); | |
| 487 | if (opts.whitespace) |ws| jsw.whitespace = ws; | |
| 488 | try jsw.beginArray(); | |
| 489 | inline for (comptime std.meta.fields(Decl)) |f| { | |
| 490 | try jsw.arrayElem(); | |
| 491 | try std.json.stringify(@field(self, f.name), opts, w); | |
| 492 | jsw.state_index -= 1; | |
| 493 | } | |
| 494 | try jsw.endArray(); | |
| 495 | } | |
| 477 | 496 | }; |
| 478 | 497 | |
| 479 | 498 | const AstNode = struct { |
| ... | ... | @@ -485,6 +504,22 @@ const DocData = struct { |
| 485 | 504 | docs: ?[]const u8 = null, |
| 486 | 505 | fields: ?[]usize = null, // index into astNodes |
| 487 | 506 | @"comptime": bool = false, |
| 507 | ||
| 508 | pub fn jsonStringify( | |
| 509 | self: AstNode, | |
| 510 | opts: std.json.StringifyOptions, | |
| 511 | w: anytype, | |
| 512 | ) !void { | |
| 513 | var jsw = std.json.writeStream(w, 15); | |
| 514 | if (opts.whitespace) |ws| jsw.whitespace = ws; | |
| 515 | try jsw.beginArray(); | |
| 516 | inline for (comptime std.meta.fields(AstNode)) |f| { | |
| 517 | try jsw.arrayElem(); | |
| 518 | try std.json.stringify(@field(self, f.name), opts, w); | |
| 519 | jsw.state_index -= 1; | |
| 520 | } | |
| 521 | try jsw.endArray(); | |
| 522 | } | |
| 488 | 523 | }; |
| 489 | 524 | |
| 490 | 525 | const Type = union(enum) { |
| ... | ... | @@ -525,7 +560,6 @@ const DocData = struct { |
| 525 | 560 | fields: ?[]Expr = null, // (use src->fields to find names) |
| 526 | 561 | line_number: usize, |
| 527 | 562 | outer_decl: usize, |
| 528 | ast: usize, | |
| 529 | 563 | }, |
| 530 | 564 | ComptimeExpr: struct { name: []const u8 }, |
| 531 | 565 | ComptimeFloat: struct { name: []const u8 }, |
| ... | ... | @@ -548,7 +582,6 @@ const DocData = struct { |
| 548 | 582 | src: usize, // index into astNodes |
| 549 | 583 | privDecls: []usize = &.{}, // index into decls |
| 550 | 584 | pubDecls: []usize = &.{}, // index into decls |
| 551 | ast: usize, | |
| 552 | 585 | // (use src->fields to find field names) |
| 553 | 586 | }, |
| 554 | 587 | Union: struct { |
| ... | ... | @@ -557,7 +590,6 @@ const DocData = struct { |
| 557 | 590 | privDecls: []usize = &.{}, // index into decls |
| 558 | 591 | pubDecls: []usize = &.{}, // index into decls |
| 559 | 592 | fields: []Expr = &.{}, // (use src->fields to find names) |
| 560 | ast: usize, | |
| 561 | 593 | }, |
| 562 | 594 | Fn: struct { |
| 563 | 595 | name: []const u8, |
| ... | ... | @@ -582,7 +614,6 @@ const DocData = struct { |
| 582 | 614 | src: usize, // index into astNodes |
| 583 | 615 | privDecls: []usize = &.{}, // index into decls |
| 584 | 616 | pubDecls: []usize = &.{}, // index into decls |
| 585 | ast: usize, | |
| 586 | 617 | }, |
| 587 | 618 | Frame: struct { name: []const u8 }, |
| 588 | 619 | AnyFrame: struct { name: []const u8 }, |
| ... | ... | @@ -601,14 +632,15 @@ const DocData = struct { |
| 601 | 632 | ) !void { |
| 602 | 633 | const active_tag = std.meta.activeTag(self); |
| 603 | 634 | var jsw = std.json.writeStream(w, 15); |
| 604 | try jsw.beginObject(); | |
| 605 | try jsw.objectField("kind"); | |
| 635 | if (opts.whitespace) |ws| jsw.whitespace = ws; | |
| 636 | try jsw.beginArray(); | |
| 637 | try jsw.arrayElem(); | |
| 606 | 638 | try jsw.emitNumber(@enumToInt(active_tag)); |
| 607 | 639 | inline for (comptime std.meta.fields(Type)) |case| { |
| 608 | 640 | if (@field(Type, case.name) == active_tag) { |
| 609 | 641 | const current_value = @field(self, case.name); |
| 610 | 642 | inline for (comptime std.meta.fields(case.field_type)) |f| { |
| 611 | try jsw.objectField(f.name); | |
| 643 | try jsw.arrayElem(); | |
| 612 | 644 | if (f.field_type == std.builtin.TypeInfo.Pointer.Size) { |
| 613 | 645 | try jsw.emitNumber(@enumToInt(@field(current_value, f.name))); |
| 614 | 646 | } else { |
| ... | ... | @@ -618,7 +650,7 @@ const DocData = struct { |
| 618 | 650 | } |
| 619 | 651 | } |
| 620 | 652 | } |
| 621 | try jsw.endObject(); | |
| 653 | try jsw.endArray(); | |
| 622 | 654 | } |
| 623 | 655 | }; |
| 624 | 656 | |
| ... | ... | @@ -686,7 +718,7 @@ const DocData = struct { |
| 686 | 718 | const SwitchOp = struct { |
| 687 | 719 | cond_index: usize, |
| 688 | 720 | file_name: []const u8, |
| 689 | ast: usize, | |
| 721 | src: usize, | |
| 690 | 722 | outer_decl: usize, // index in `types` |
| 691 | 723 | }; |
| 692 | 724 | const BuiltinBin = struct { |
| ... | ... | @@ -704,7 +736,15 @@ const DocData = struct { |
| 704 | 736 | end: ?usize = null, |
| 705 | 737 | sentinel: ?usize = null, // index in `exprs` |
| 706 | 738 | }; |
| 707 | const Cmpxchg = struct { name: []const u8, type: usize, ptr: usize, expected_value: usize, new_value: usize, success_order: usize, failure_order: usize }; | |
| 739 | const Cmpxchg = struct { | |
| 740 | name: []const u8, | |
| 741 | type: usize, | |
| 742 | ptr: usize, | |
| 743 | expected_value: usize, | |
| 744 | new_value: usize, | |
| 745 | success_order: usize, | |
| 746 | failure_order: usize, | |
| 747 | }; | |
| 708 | 748 | const As = struct { |
| 709 | 749 | typeRefArg: ?usize, // index in `exprs` |
| 710 | 750 | exprArg: usize, // index in `exprs` |
| ... | ... | @@ -721,11 +761,12 @@ const DocData = struct { |
| 721 | 761 | |
| 722 | 762 | pub fn jsonStringify( |
| 723 | 763 | self: Expr, |
| 724 | opt: std.json.StringifyOptions, | |
| 764 | opts: std.json.StringifyOptions, | |
| 725 | 765 | w: anytype, |
| 726 | 766 | ) !void { |
| 727 | 767 | const active_tag = std.meta.activeTag(self); |
| 728 | 768 | var jsw = std.json.writeStream(w, 15); |
| 769 | if (opts.whitespace) |ws| jsw.whitespace = ws; | |
| 729 | 770 | try jsw.beginObject(); |
| 730 | 771 | try jsw.objectField(@tagName(active_tag)); |
| 731 | 772 | switch (self) { |
| ... | ... | @@ -742,7 +783,7 @@ const DocData = struct { |
| 742 | 783 | if (comptime std.mem.eql(u8, case.name, "builtinField")) |
| 743 | 784 | continue; |
| 744 | 785 | if (@field(Expr, case.name) == active_tag) { |
| 745 | try std.json.stringify(@field(self, case.name), opt, w); | |
| 786 | try std.json.stringify(@field(self, case.name), opts, w); | |
| 746 | 787 | jsw.state_index -= 1; |
| 747 | 788 | // TODO: we should not reach into the state of the |
| 748 | 789 | // json writer, but alas, this is what's |
| ... | ... | @@ -1874,7 +1915,12 @@ fn walkInstruction( |
| 1874 | 1915 | // log.debug("{s}", .{sep}); |
| 1875 | 1916 | |
| 1876 | 1917 | const switch_index = self.exprs.items.len; |
| 1877 | try self.exprs.append(self.arena, .{ .switchOp = .{ .cond_index = cond_index, .file_name = file.sub_file_path, .ast = ast_index, .outer_decl = type_index } }); | |
| 1918 | try self.exprs.append(self.arena, .{ .switchOp = .{ | |
| 1919 | .cond_index = cond_index, | |
| 1920 | .file_name = file.sub_file_path, | |
| 1921 | .src = ast_index, | |
| 1922 | .outer_decl = type_index, | |
| 1923 | } }); | |
| 1878 | 1924 | |
| 1879 | 1925 | return DocData.WalkResult{ |
| 1880 | 1926 | .typeRef = .{ .type = @enumToInt(Ref.type_type) }, |
| ... | ... | @@ -2505,7 +2551,6 @@ fn walkInstruction( |
| 2505 | 2551 | .src = self_ast_node_index, |
| 2506 | 2552 | .privDecls = priv_decl_indexes.items, |
| 2507 | 2553 | .pubDecls = decl_indexes.items, |
| 2508 | .ast = self_ast_node_index, | |
| 2509 | 2554 | }, |
| 2510 | 2555 | }; |
| 2511 | 2556 | if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| { |
| ... | ... | @@ -2644,7 +2689,13 @@ fn walkInstruction( |
| 2644 | 2689 | self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items; |
| 2645 | 2690 | |
| 2646 | 2691 | self.types.items[type_slot_index] = .{ |
| 2647 | .Union = .{ .name = "todo_name", .src = self_ast_node_index, .privDecls = priv_decl_indexes.items, .pubDecls = decl_indexes.items, .fields = field_type_refs.items, .ast = self_ast_node_index }, | |
| 2692 | .Union = .{ | |
| 2693 | .name = "todo_name", | |
| 2694 | .src = self_ast_node_index, | |
| 2695 | .privDecls = priv_decl_indexes.items, | |
| 2696 | .pubDecls = decl_indexes.items, | |
| 2697 | .fields = field_type_refs.items, | |
| 2698 | }, | |
| 2648 | 2699 | }; |
| 2649 | 2700 | |
| 2650 | 2701 | if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| { |
| ... | ... | @@ -2796,7 +2847,12 @@ fn walkInstruction( |
| 2796 | 2847 | self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items; |
| 2797 | 2848 | |
| 2798 | 2849 | self.types.items[type_slot_index] = .{ |
| 2799 | .Enum = .{ .name = "todo_name", .src = self_ast_node_index, .privDecls = priv_decl_indexes.items, .pubDecls = decl_indexes.items, .ast = self_ast_node_index }, | |
| 2850 | .Enum = .{ | |
| 2851 | .name = "todo_name", | |
| 2852 | .src = self_ast_node_index, | |
| 2853 | .privDecls = priv_decl_indexes.items, | |
| 2854 | .pubDecls = decl_indexes.items, | |
| 2855 | }, | |
| 2800 | 2856 | }; |
| 2801 | 2857 | if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| { |
| 2802 | 2858 | for (paths.items) |resume_info| { |
| ... | ... | @@ -2910,7 +2966,15 @@ fn walkInstruction( |
| 2910 | 2966 | self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items; |
| 2911 | 2967 | |
| 2912 | 2968 | self.types.items[type_slot_index] = .{ |
| 2913 | .Struct = .{ .name = "todo_name", .src = self_ast_node_index, .privDecls = priv_decl_indexes.items, .pubDecls = decl_indexes.items, .fields = field_type_refs.items, .line_number = self.ast_nodes.items[self_ast_node_index].line, .outer_decl = type_slot_index - 1, .ast = self_ast_node_index }, | |
| 2969 | .Struct = .{ | |
| 2970 | .name = "todo_name", | |
| 2971 | .src = self_ast_node_index, | |
| 2972 | .privDecls = priv_decl_indexes.items, | |
| 2973 | .pubDecls = decl_indexes.items, | |
| 2974 | .fields = field_type_refs.items, | |
| 2975 | .line_number = self.ast_nodes.items[self_ast_node_index].line, | |
| 2976 | .outer_decl = type_slot_index - 1, | |
| 2977 | }, | |
| 2914 | 2978 | }; |
| 2915 | 2979 | if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| { |
| 2916 | 2980 | for (paths.items) |resume_info| { |
src/Compilation.zig+87-65| ... | ... | @@ -1238,7 +1238,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1238 | 1238 | options.target, |
| 1239 | 1239 | options.is_native_abi, |
| 1240 | 1240 | link_libc, |
| 1241 | options.system_lib_names.len != 0 or options.frameworks.count() != 0, | |
| 1242 | 1241 | options.libc_installation, |
| 1243 | 1242 | options.native_darwin_sdk != null, |
| 1244 | 1243 | ); |
| ... | ... | @@ -4522,7 +4521,6 @@ fn detectLibCIncludeDirs( |
| 4522 | 4521 | target: Target, |
| 4523 | 4522 | is_native_abi: bool, |
| 4524 | 4523 | link_libc: bool, |
| 4525 | link_system_libs: bool, | |
| 4526 | 4524 | libc_installation: ?*const LibCInstallation, |
| 4527 | 4525 | has_macos_sdk: bool, |
| 4528 | 4526 | ) !LibCDirs { |
| ... | ... | @@ -4539,7 +4537,7 @@ fn detectLibCIncludeDirs( |
| 4539 | 4537 | |
| 4540 | 4538 | // If linking system libraries and targeting the native abi, default to |
| 4541 | 4539 | // using the system libc installation. |
| 4542 | if (link_system_libs and is_native_abi and !target.isMinGW()) { | |
| 4540 | if (is_native_abi and !target.isMinGW()) { | |
| 4543 | 4541 | if (target.isDarwin()) { |
| 4544 | 4542 | return if (has_macos_sdk) |
| 4545 | 4543 | // For Darwin/macOS, we are all set with getDarwinSDK found earlier. |
| ... | ... | @@ -4551,74 +4549,29 @@ fn detectLibCIncludeDirs( |
| 4551 | 4549 | getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target); |
| 4552 | 4550 | } |
| 4553 | 4551 | const libc = try arena.create(LibCInstallation); |
| 4554 | libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true }); | |
| 4552 | libc.* = LibCInstallation.findNative(.{ .allocator = arena }) catch |err| switch (err) { | |
| 4553 | error.CCompilerExitCode, | |
| 4554 | error.CCompilerCrashed, | |
| 4555 | error.CCompilerCannotFindHeaders, | |
| 4556 | error.UnableToSpawnCCompiler, | |
| 4557 | => |e| { | |
| 4558 | // We tried to integrate with the native system C compiler, | |
| 4559 | // however, it is not installed. So we must rely on our bundled | |
| 4560 | // libc files. | |
| 4561 | if (target_util.canBuildLibC(target)) { | |
| 4562 | return detectLibCFromBuilding(arena, zig_lib_dir, target, has_macos_sdk); | |
| 4563 | } | |
| 4564 | return e; | |
| 4565 | }, | |
| 4566 | else => |e| return e, | |
| 4567 | }; | |
| 4555 | 4568 | return detectLibCFromLibCInstallation(arena, target, libc); |
| 4556 | 4569 | } |
| 4557 | 4570 | |
| 4558 | 4571 | // If not linking system libraries, build and provide our own libc by |
| 4559 | 4572 | // default if possible. |
| 4560 | 4573 | if (target_util.canBuildLibC(target)) { |
| 4561 | switch (target.os.tag) { | |
| 4562 | .macos => return if (has_macos_sdk) | |
| 4563 | // For Darwin/macOS, we are all set with getDarwinSDK found earlier. | |
| 4564 | LibCDirs{ | |
| 4565 | .libc_include_dir_list = &[0][]u8{}, | |
| 4566 | .libc_installation = null, | |
| 4567 | } | |
| 4568 | else | |
| 4569 | getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target), | |
| 4570 | else => { | |
| 4571 | const generic_name = target_util.libCGenericName(target); | |
| 4572 | // Some architectures are handled by the same set of headers. | |
| 4573 | const arch_name = if (target.abi.isMusl()) | |
| 4574 | musl.archName(target.cpu.arch) | |
| 4575 | else if (target.cpu.arch.isThumb()) | |
| 4576 | // ARM headers are valid for Thumb too. | |
| 4577 | switch (target.cpu.arch) { | |
| 4578 | .thumb => "arm", | |
| 4579 | .thumbeb => "armeb", | |
| 4580 | else => unreachable, | |
| 4581 | } | |
| 4582 | else | |
| 4583 | @tagName(target.cpu.arch); | |
| 4584 | const os_name = @tagName(target.os.tag); | |
| 4585 | // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name. | |
| 4586 | const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi); | |
| 4587 | const s = std.fs.path.sep_str; | |
| 4588 | const arch_include_dir = try std.fmt.allocPrint( | |
| 4589 | arena, | |
| 4590 | "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", | |
| 4591 | .{ zig_lib_dir, arch_name, os_name, abi_name }, | |
| 4592 | ); | |
| 4593 | const generic_include_dir = try std.fmt.allocPrint( | |
| 4594 | arena, | |
| 4595 | "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}", | |
| 4596 | .{ zig_lib_dir, generic_name }, | |
| 4597 | ); | |
| 4598 | const generic_arch_name = target_util.osArchName(target); | |
| 4599 | const arch_os_include_dir = try std.fmt.allocPrint( | |
| 4600 | arena, | |
| 4601 | "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any", | |
| 4602 | .{ zig_lib_dir, generic_arch_name, os_name }, | |
| 4603 | ); | |
| 4604 | const generic_os_include_dir = try std.fmt.allocPrint( | |
| 4605 | arena, | |
| 4606 | "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", | |
| 4607 | .{ zig_lib_dir, os_name }, | |
| 4608 | ); | |
| 4609 | ||
| 4610 | const list = try arena.alloc([]const u8, 4); | |
| 4611 | list[0] = arch_include_dir; | |
| 4612 | list[1] = generic_include_dir; | |
| 4613 | list[2] = arch_os_include_dir; | |
| 4614 | list[3] = generic_os_include_dir; | |
| 4615 | ||
| 4616 | return LibCDirs{ | |
| 4617 | .libc_include_dir_list = list, | |
| 4618 | .libc_installation = null, | |
| 4619 | }; | |
| 4620 | }, | |
| 4621 | } | |
| 4574 | return detectLibCFromBuilding(arena, zig_lib_dir, target, has_macos_sdk); | |
| 4622 | 4575 | } |
| 4623 | 4576 | |
| 4624 | 4577 | // If zig can't build the libc for the target and we are targeting the |
| ... | ... | @@ -4677,6 +4630,75 @@ fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const |
| 4677 | 4630 | }; |
| 4678 | 4631 | } |
| 4679 | 4632 | |
| 4633 | fn detectLibCFromBuilding( | |
| 4634 | arena: Allocator, | |
| 4635 | zig_lib_dir: []const u8, | |
| 4636 | target: std.Target, | |
| 4637 | has_macos_sdk: bool, | |
| 4638 | ) !LibCDirs { | |
| 4639 | switch (target.os.tag) { | |
| 4640 | .macos => return if (has_macos_sdk) | |
| 4641 | // For Darwin/macOS, we are all set with getDarwinSDK found earlier. | |
| 4642 | LibCDirs{ | |
| 4643 | .libc_include_dir_list = &[0][]u8{}, | |
| 4644 | .libc_installation = null, | |
| 4645 | } | |
| 4646 | else | |
| 4647 | getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target), | |
| 4648 | else => { | |
| 4649 | const generic_name = target_util.libCGenericName(target); | |
| 4650 | // Some architectures are handled by the same set of headers. | |
| 4651 | const arch_name = if (target.abi.isMusl()) | |
| 4652 | musl.archName(target.cpu.arch) | |
| 4653 | else if (target.cpu.arch.isThumb()) | |
| 4654 | // ARM headers are valid for Thumb too. | |
| 4655 | switch (target.cpu.arch) { | |
| 4656 | .thumb => "arm", | |
| 4657 | .thumbeb => "armeb", | |
| 4658 | else => unreachable, | |
| 4659 | } | |
| 4660 | else | |
| 4661 | @tagName(target.cpu.arch); | |
| 4662 | const os_name = @tagName(target.os.tag); | |
| 4663 | // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name. | |
| 4664 | const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi); | |
| 4665 | const s = std.fs.path.sep_str; | |
| 4666 | const arch_include_dir = try std.fmt.allocPrint( | |
| 4667 | arena, | |
| 4668 | "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", | |
| 4669 | .{ zig_lib_dir, arch_name, os_name, abi_name }, | |
| 4670 | ); | |
| 4671 | const generic_include_dir = try std.fmt.allocPrint( | |
| 4672 | arena, | |
| 4673 | "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}", | |
| 4674 | .{ zig_lib_dir, generic_name }, | |
| 4675 | ); | |
| 4676 | const generic_arch_name = target_util.osArchName(target); | |
| 4677 | const arch_os_include_dir = try std.fmt.allocPrint( | |
| 4678 | arena, | |
| 4679 | "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any", | |
| 4680 | .{ zig_lib_dir, generic_arch_name, os_name }, | |
| 4681 | ); | |
| 4682 | const generic_os_include_dir = try std.fmt.allocPrint( | |
| 4683 | arena, | |
| 4684 | "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", | |
| 4685 | .{ zig_lib_dir, os_name }, | |
| 4686 | ); | |
| 4687 | ||
| 4688 | const list = try arena.alloc([]const u8, 4); | |
| 4689 | list[0] = arch_include_dir; | |
| 4690 | list[1] = generic_include_dir; | |
| 4691 | list[2] = arch_os_include_dir; | |
| 4692 | list[3] = generic_os_include_dir; | |
| 4693 | ||
| 4694 | return LibCDirs{ | |
| 4695 | .libc_include_dir_list = list, | |
| 4696 | .libc_installation = null, | |
| 4697 | }; | |
| 4698 | }, | |
| 4699 | } | |
| 4700 | } | |
| 4701 | ||
| 4680 | 4702 | pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 { |
| 4681 | 4703 | if (comp.wantBuildGLibCFromSource() or |
| 4682 | 4704 | comp.wantBuildMuslFromSource() or |
src/arch/aarch64/CodeGen.zig+35-50| ... | ... | @@ -139,21 +139,10 @@ const MCValue = union(enum) { |
| 139 | 139 | /// If the type is a pointer, it means the pointer address is at |
| 140 | 140 | /// this memory location. |
| 141 | 141 | memory: u64, |
| 142 | /// The value is in memory referenced indirectly via a GOT entry | |
| 143 | /// index. | |
| 144 | /// | |
| 145 | /// If the type is a pointer, it means the pointer is referenced | |
| 146 | /// indirectly via GOT. When lowered, linker will emit | |
| 147 | /// relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and | |
| 148 | /// ARM64_RELOC_GOT_LOAD_PAGEOFF12. | |
| 149 | got_load: u32, | |
| 150 | /// The value is in memory referenced directly via symbol index. | |
| 151 | /// | |
| 152 | /// If the type is a pointer, it means the pointer is referenced | |
| 153 | /// directly via symbol index. When lowered, linker will emit a | |
| 154 | /// relocation of type ARM64_RELOC_PAGE21 and | |
| 155 | /// ARM64_RELOC_PAGEOFF12. | |
| 156 | direct_load: u32, | |
| 142 | /// The value is in memory but requires a linker relocation fixup: | |
| 143 | /// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc) | |
| 144 | /// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc) | |
| 145 | linker_load: struct { @"type": enum { got, direct }, sym_index: u32 }, | |
| 157 | 146 | /// The value is one of the stack variables. |
| 158 | 147 | /// |
| 159 | 148 | /// If the type is a pointer, it means the pointer address is in |
| ... | ... | @@ -2959,8 +2948,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2959 | 2948 | .memory, |
| 2960 | 2949 | .stack_offset, |
| 2961 | 2950 | .stack_argument_offset, |
| 2962 | .got_load, | |
| 2963 | .direct_load, | |
| 2951 | .linker_load, | |
| 2964 | 2952 | => { |
| 2965 | 2953 | const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 2966 | 2954 | try self.load(dst_mcv, .{ .register = addr_reg }, ptr_ty); |
| ... | ... | @@ -3197,8 +3185,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3197 | 3185 | .memory, |
| 3198 | 3186 | .stack_offset, |
| 3199 | 3187 | .stack_argument_offset, |
| 3200 | .got_load, | |
| 3201 | .direct_load, | |
| 3188 | .linker_load, | |
| 3202 | 3189 | => { |
| 3203 | 3190 | const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 3204 | 3191 | try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty); |
| ... | ... | @@ -3493,7 +3480,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3493 | 3480 | const func = func_payload.data; |
| 3494 | 3481 | const fn_owner_decl = mod.declPtr(func.owner_decl); |
| 3495 | 3482 | try self.genSetReg(Type.initTag(.u64), .x30, .{ |
| 3496 | .got_load = fn_owner_decl.link.macho.sym_index, | |
| 3483 | .linker_load = .{ | |
| 3484 | .@"type" = .got, | |
| 3485 | .sym_index = fn_owner_decl.link.macho.sym_index, | |
| 3486 | }, | |
| 3497 | 3487 | }); |
| 3498 | 3488 | // blr x30 |
| 3499 | 3489 | _ = try self.addInst(.{ |
| ... | ... | @@ -4427,8 +4417,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4427 | 4417 | .register = cond_reg, |
| 4428 | 4418 | }); |
| 4429 | 4419 | }, |
| 4430 | .got_load, | |
| 4431 | .direct_load, | |
| 4420 | .linker_load, | |
| 4432 | 4421 | .memory, |
| 4433 | 4422 | .stack_argument_offset, |
| 4434 | 4423 | .stack_offset, |
| ... | ... | @@ -4479,13 +4468,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4479 | 4468 | }); |
| 4480 | 4469 | }, |
| 4481 | 4470 | .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = addr }), |
| 4482 | .got_load, | |
| 4483 | .direct_load, | |
| 4484 | => |sym_index| { | |
| 4485 | const tag: Mir.Inst.Tag = switch (mcv) { | |
| 4486 | .got_load => .load_memory_ptr_got, | |
| 4487 | .direct_load => .load_memory_ptr_direct, | |
| 4488 | else => unreachable, | |
| 4471 | .linker_load => |load_struct| { | |
| 4472 | const tag: Mir.Inst.Tag = switch (load_struct.@"type") { | |
| 4473 | .got => .load_memory_ptr_got, | |
| 4474 | .direct => .load_memory_ptr_direct, | |
| 4489 | 4475 | }; |
| 4490 | 4476 | const mod = self.bin_file.options.module.?; |
| 4491 | 4477 | _ = try self.addInst(.{ |
| ... | ... | @@ -4494,7 +4480,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4494 | 4480 | .payload = try self.addExtra(Mir.LoadMemoryPie{ |
| 4495 | 4481 | .register = @enumToInt(src_reg), |
| 4496 | 4482 | .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index, |
| 4497 | .sym_index = sym_index, | |
| 4483 | .sym_index = load_struct.sym_index, | |
| 4498 | 4484 | }), |
| 4499 | 4485 | }, |
| 4500 | 4486 | }); |
| ... | ... | @@ -4594,13 +4580,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4594 | 4580 | }); |
| 4595 | 4581 | }, |
| 4596 | 4582 | .register_with_overflow => unreachable, // doesn't fit into a register |
| 4597 | .got_load, | |
| 4598 | .direct_load, | |
| 4599 | => |sym_index| { | |
| 4600 | const tag: Mir.Inst.Tag = switch (mcv) { | |
| 4601 | .got_load => .load_memory_got, | |
| 4602 | .direct_load => .load_memory_direct, | |
| 4603 | else => unreachable, | |
| 4583 | .linker_load => |load_struct| { | |
| 4584 | const tag: Mir.Inst.Tag = switch (load_struct.@"type") { | |
| 4585 | .got => .load_memory_got, | |
| 4586 | .direct => .load_memory_direct, | |
| 4604 | 4587 | }; |
| 4605 | 4588 | const mod = self.bin_file.options.module.?; |
| 4606 | 4589 | _ = try self.addInst(.{ |
| ... | ... | @@ -4609,7 +4592,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4609 | 4592 | .payload = try self.addExtra(Mir.LoadMemoryPie{ |
| 4610 | 4593 | .register = @enumToInt(reg), |
| 4611 | 4594 | .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index, |
| 4612 | .sym_index = sym_index, | |
| 4595 | .sym_index = load_struct.sym_index, | |
| 4613 | 4596 | }), |
| 4614 | 4597 | }, |
| 4615 | 4598 | }); |
| ... | ... | @@ -4741,8 +4724,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4741 | 4724 | .register_with_overflow => { |
| 4742 | 4725 | return self.fail("TODO implement genSetStackArgument {}", .{mcv}); |
| 4743 | 4726 | }, |
| 4744 | .got_load, | |
| 4745 | .direct_load, | |
| 4727 | .linker_load, | |
| 4746 | 4728 | .memory, |
| 4747 | 4729 | .stack_argument_offset, |
| 4748 | 4730 | .stack_offset, |
| ... | ... | @@ -4785,13 +4767,10 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4785 | 4767 | }); |
| 4786 | 4768 | }, |
| 4787 | 4769 | .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }), |
| 4788 | .got_load, | |
| 4789 | .direct_load, | |
| 4790 | => |sym_index| { | |
| 4791 | const tag: Mir.Inst.Tag = switch (mcv) { | |
| 4792 | .got_load => .load_memory_ptr_got, | |
| 4793 | .direct_load => .load_memory_ptr_direct, | |
| 4794 | else => unreachable, | |
| 4770 | .linker_load => |load_struct| { | |
| 4771 | const tag: Mir.Inst.Tag = switch (load_struct.@"type") { | |
| 4772 | .got => .load_memory_ptr_got, | |
| 4773 | .direct => .load_memory_ptr_direct, | |
| 4795 | 4774 | }; |
| 4796 | 4775 | const mod = self.bin_file.options.module.?; |
| 4797 | 4776 | _ = try self.addInst(.{ |
| ... | ... | @@ -4800,7 +4779,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4800 | 4779 | .payload = try self.addExtra(Mir.LoadMemoryPie{ |
| 4801 | 4780 | .register = @enumToInt(src_reg), |
| 4802 | 4781 | .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index, |
| 4803 | .sym_index = sym_index, | |
| 4782 | .sym_index = load_struct.sym_index, | |
| 4804 | 4783 | }), |
| 4805 | 4784 | }, |
| 4806 | 4785 | }); |
| ... | ... | @@ -5107,7 +5086,10 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 5107 | 5086 | // Because MachO is PIE-always-on, we defer memory address resolution until |
| 5108 | 5087 | // the linker has enough info to perform relocations. |
| 5109 | 5088 | assert(decl.link.macho.sym_index != 0); |
| 5110 | return MCValue{ .got_load = decl.link.macho.sym_index }; | |
| 5089 | return MCValue{ .linker_load = .{ | |
| 5090 | .@"type" = .got, | |
| 5091 | .sym_index = decl.link.macho.sym_index, | |
| 5092 | } }; | |
| 5111 | 5093 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 5112 | 5094 | return self.fail("TODO codegen COFF const Decl pointer", .{}); |
| 5113 | 5095 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| ... | ... | @@ -5129,7 +5111,10 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 5129 | 5111 | const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; |
| 5130 | 5112 | return MCValue{ .memory = vaddr }; |
| 5131 | 5113 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 5132 | return MCValue{ .direct_load = local_sym_index }; | |
| 5114 | return MCValue{ .linker_load = .{ | |
| 5115 | .@"type" = .direct, | |
| 5116 | .sym_index = local_sym_index, | |
| 5117 | } }; | |
| 5133 | 5118 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 5134 | 5119 | return self.fail("TODO lower unnamed const in COFF", .{}); |
| 5135 | 5120 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { |
src/arch/aarch64/Emit.zig+2-4| ... | ... | @@ -681,12 +681,10 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 681 | 681 | }; |
| 682 | 682 | // Add relocation to the decl. |
| 683 | 683 | const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?; |
| 684 | const target = macho_file.getGlobalByIndex(relocation.sym_index); | |
| 684 | 685 | try atom.relocs.append(emit.bin_file.allocator, .{ |
| 685 | 686 | .offset = offset, |
| 686 | .target = .{ | |
| 687 | .sym_index = relocation.sym_index, | |
| 688 | .file = null, | |
| 689 | }, | |
| 687 | .target = target, | |
| 690 | 688 | .addend = 0, |
| 691 | 689 | .subtractor = null, |
| 692 | 690 | .pcrel = true, |
src/arch/arm/CodeGen.zig+1558-1057| ... | ... | @@ -438,9 +438,8 @@ fn gen(self: *Self) !void { |
| 438 | 438 | // mov fp, sp |
| 439 | 439 | _ = try self.addInst(.{ |
| 440 | 440 | .tag = .mov, |
| 441 | .data = .{ .rr_op = .{ | |
| 441 | .data = .{ .r_op_mov = .{ | |
| 442 | 442 | .rd = .fp, |
| 443 | .rn = .r0, | |
| 444 | 443 | .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none), |
| 445 | 444 | } }, |
| 446 | 445 | }); |
| ... | ... | @@ -452,9 +451,7 @@ fn gen(self: *Self) !void { |
| 452 | 451 | // The address of where to store the return value is in |
| 453 | 452 | // r0. As this register might get overwritten along the |
| 454 | 453 | // way, save the address to the stack. |
| 455 | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, 4) + 4; | |
| 456 | self.next_stack_offset = stack_offset; | |
| 457 | self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); | |
| 454 | const stack_offset = try self.allocMem(4, 4, null); | |
| 458 | 455 | |
| 459 | 456 | try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 }); |
| 460 | 457 | self.ret_mcv = MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -491,14 +488,10 @@ fn gen(self: *Self) !void { |
| 491 | 488 | const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); |
| 492 | 489 | const stack_size = aligned_total_stack_end - self.saved_regs_stack_space; |
| 493 | 490 | self.max_end_stack = stack_size; |
| 494 | if (Instruction.Operand.fromU32(stack_size)) |op| { | |
| 495 | self.mir_instructions.set(sub_reloc, .{ | |
| 496 | .tag = .sub, | |
| 497 | .data = .{ .rr_op = .{ .rd = .sp, .rn = .sp, .op = op } }, | |
| 498 | }); | |
| 499 | } else { | |
| 500 | return self.failSymbol("TODO ARM: allow larger stacks", .{}); | |
| 501 | } | |
| 491 | self.mir_instructions.set(sub_reloc, .{ | |
| 492 | .tag = .sub_sp_scratch_r0, | |
| 493 | .data = .{ .imm32 = stack_size }, | |
| 494 | }); | |
| 502 | 495 | |
| 503 | 496 | _ = try self.addInst(.{ |
| 504 | 497 | .tag = .dbg_epilogue_begin, |
| ... | ... | @@ -531,9 +524,8 @@ fn gen(self: *Self) !void { |
| 531 | 524 | // mov sp, fp |
| 532 | 525 | _ = try self.addInst(.{ |
| 533 | 526 | .tag = .mov, |
| 534 | .data = .{ .rr_op = .{ | |
| 527 | .data = .{ .r_op_mov = .{ | |
| 535 | 528 | .rd = .sp, |
| 536 | .rn = .r0, | |
| 537 | 529 | .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none), |
| 538 | 530 | } }, |
| 539 | 531 | }); |
| ... | ... | @@ -895,17 +887,30 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { |
| 895 | 887 | try table.ensureUnusedCapacity(self.gpa, additional_count); |
| 896 | 888 | } |
| 897 | 889 | |
| 898 | fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { | |
| 890 | fn allocMem( | |
| 891 | self: *Self, | |
| 892 | abi_size: u32, | |
| 893 | abi_align: u32, | |
| 894 | maybe_inst: ?Air.Inst.Index, | |
| 895 | ) !u32 { | |
| 896 | assert(abi_size > 0); | |
| 897 | assert(abi_align > 0); | |
| 898 | ||
| 899 | 899 | if (abi_align > self.stack_align) |
| 900 | 900 | self.stack_align = abi_align; |
| 901 | ||
| 901 | 902 | // TODO find a free slot instead of always appending |
| 902 | 903 | const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; |
| 903 | 904 | self.next_stack_offset = offset; |
| 904 | 905 | self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); |
| 905 | try self.stack.putNoClobber(self.gpa, offset, .{ | |
| 906 | .inst = inst, | |
| 907 | .size = abi_size, | |
| 908 | }); | |
| 906 | ||
| 907 | if (maybe_inst) |inst| { | |
| 908 | try self.stack.putNoClobber(self.gpa, offset, .{ | |
| 909 | .inst = inst, | |
| 910 | .size = abi_size, | |
| 911 | }); | |
| 912 | } | |
| 913 | ||
| 909 | 914 | return offset; |
| 910 | 915 | } |
| 911 | 916 | |
| ... | ... | @@ -927,35 +932,34 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 927 | 932 | }; |
| 928 | 933 | // TODO swap this for inst.ty.ptrAlign |
| 929 | 934 | const abi_align = elem_ty.abiAlignment(self.target.*); |
| 930 | return self.allocMem(inst, abi_size, abi_align); | |
| 935 | ||
| 936 | return self.allocMem(abi_size, abi_align, inst); | |
| 931 | 937 | } |
| 932 | 938 | |
| 933 | fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { | |
| 934 | const elem_ty = self.air.typeOfIndex(inst); | |
| 939 | fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue { | |
| 935 | 940 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { |
| 936 | 941 | const mod = self.bin_file.options.module.?; |
| 937 | 942 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); |
| 938 | 943 | }; |
| 939 | 944 | const abi_align = elem_ty.abiAlignment(self.target.*); |
| 940 | if (abi_align > self.stack_align) | |
| 941 | self.stack_align = abi_align; | |
| 942 | 945 | |
| 943 | 946 | if (reg_ok) { |
| 944 | 947 | // Make sure the type can fit in a register before we try to allocate one. |
| 945 | 948 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 946 | 949 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 947 | 950 | if (abi_size <= ptr_bytes) { |
| 948 | if (self.register_manager.tryAllocReg(inst, gp)) |reg| { | |
| 951 | if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| { | |
| 949 | 952 | return MCValue{ .register = reg }; |
| 950 | 953 | } |
| 951 | 954 | } |
| 952 | 955 | } |
| 953 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); | |
| 956 | ||
| 957 | const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst); | |
| 954 | 958 | return MCValue{ .stack_offset = stack_offset }; |
| 955 | 959 | } |
| 956 | 960 | |
| 957 | 961 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 958 | const stack_mcv = try self.allocRegOrMem(inst, false); | |
| 962 | const stack_mcv = try self.allocRegOrMem(self.air.typeOfIndex(inst), false, inst); | |
| 959 | 963 | log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv }); |
| 960 | 964 | |
| 961 | 965 | const reg_mcv = self.getResolvedInstValue(inst); |
| ... | ... | @@ -976,12 +980,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 976 | 980 | /// occupied |
| 977 | 981 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 978 | 982 | if (self.cpsr_flags_inst) |inst_to_save| { |
| 983 | const ty = self.air.typeOfIndex(inst_to_save); | |
| 979 | 984 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 980 | 985 | const new_mcv = switch (mcv) { |
| 981 | .cpsr_flags => try self.allocRegOrMem(inst_to_save, true), | |
| 986 | .cpsr_flags => try self.allocRegOrMem(ty, true, inst_to_save), | |
| 982 | 987 | .register_c_flag, |
| 983 | 988 | .register_v_flag, |
| 984 | => try self.allocRegOrMem(inst_to_save, false), | |
| 989 | => try self.allocRegOrMem(ty, false, inst_to_save), | |
| 985 | 990 | else => unreachable, // mcv doesn't occupy the compare flags |
| 986 | 991 | }; |
| 987 | 992 | |
| ... | ... | @@ -1112,10 +1117,11 @@ fn truncRegister( |
| 1112 | 1117 | }); |
| 1113 | 1118 | } |
| 1114 | 1119 | |
| 1120 | /// Asserts that both operand_ty and dest_ty are integer types | |
| 1115 | 1121 | fn trunc( |
| 1116 | 1122 | self: *Self, |
| 1117 | 1123 | maybe_inst: ?Air.Inst.Index, |
| 1118 | operand: MCValue, | |
| 1124 | operand_bind: ReadArg.Bind, | |
| 1119 | 1125 | operand_ty: Type, |
| 1120 | 1126 | dest_ty: Type, |
| 1121 | 1127 | ) !MCValue { |
| ... | ... | @@ -1123,39 +1129,38 @@ fn trunc( |
| 1123 | 1129 | const info_b = dest_ty.intInfo(self.target.*); |
| 1124 | 1130 | |
| 1125 | 1131 | if (info_b.bits <= 32) { |
| 1126 | const operand_reg = switch (operand) { | |
| 1127 | .register => |r| r, | |
| 1128 | else => operand_reg: { | |
| 1129 | if (info_a.bits <= 32) { | |
| 1130 | break :operand_reg try self.copyToTmpRegister(operand_ty, operand); | |
| 1131 | } else { | |
| 1132 | return self.fail("TODO load least significant word into register", .{}); | |
| 1133 | } | |
| 1134 | }, | |
| 1135 | }; | |
| 1136 | const operand_reg_lock = self.register_manager.lockReg(operand_reg); | |
| 1137 | defer if (operand_reg_lock) |reg| self.register_manager.unlockReg(reg); | |
| 1132 | if (info_a.bits > 32) { | |
| 1133 | return self.fail("TODO load least significant word into register", .{}); | |
| 1134 | } | |
| 1138 | 1135 | |
| 1139 | const dest_reg = if (maybe_inst) |inst| blk: { | |
| 1140 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1136 | var operand_reg: Register = undefined; | |
| 1137 | var dest_reg: Register = undefined; | |
| 1141 | 1138 | |
| 1142 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { | |
| 1143 | break :blk operand_reg; | |
| 1144 | } else { | |
| 1145 | break :blk try self.register_manager.allocReg(inst, gp); | |
| 1146 | } | |
| 1147 | } else try self.register_manager.allocReg(null, gp); | |
| 1139 | const read_args = [_]ReadArg{ | |
| 1140 | .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &operand_reg }, | |
| 1141 | }; | |
| 1142 | const write_args = [_]WriteArg{ | |
| 1143 | .{ .ty = dest_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 1144 | }; | |
| 1145 | try self.allocRegs( | |
| 1146 | &read_args, | |
| 1147 | &write_args, | |
| 1148 | if (maybe_inst) |inst| .{ | |
| 1149 | .corresponding_inst = inst, | |
| 1150 | .operand_mapping = &.{0}, | |
| 1151 | } else null, | |
| 1152 | ); | |
| 1148 | 1153 | |
| 1149 | 1154 | switch (info_b.bits) { |
| 1150 | 1155 | 32 => { |
| 1151 | 1156 | try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg }); |
| 1152 | return MCValue{ .register = dest_reg }; | |
| 1153 | 1157 | }, |
| 1154 | 1158 | else => { |
| 1155 | 1159 | try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); |
| 1156 | return MCValue{ .register = dest_reg }; | |
| 1157 | 1160 | }, |
| 1158 | 1161 | } |
| 1162 | ||
| 1163 | return MCValue{ .register = dest_reg }; | |
| 1159 | 1164 | } else { |
| 1160 | 1165 | return self.fail("TODO: truncate to ints > 32 bits", .{}); |
| 1161 | 1166 | } |
| ... | ... | @@ -1163,12 +1168,12 @@ fn trunc( |
| 1163 | 1168 | |
| 1164 | 1169 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 1165 | 1170 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1166 | const operand = try self.resolveInst(ty_op.operand); | |
| 1171 | const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; | |
| 1167 | 1172 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 1168 | 1173 | const dest_ty = self.air.typeOfIndex(inst); |
| 1169 | 1174 | |
| 1170 | 1175 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { |
| 1171 | break :blk try self.trunc(inst, operand, operand_ty, dest_ty); | |
| 1176 | break :blk try self.trunc(inst, operand_bind, operand_ty, dest_ty); | |
| 1172 | 1177 | }; |
| 1173 | 1178 | |
| 1174 | 1179 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | ... | @@ -1184,29 +1189,32 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 1184 | 1189 | fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1185 | 1190 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1186 | 1191 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1187 | const operand = try self.resolveInst(ty_op.operand); | |
| 1192 | const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; | |
| 1188 | 1193 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 1189 | switch (operand) { | |
| 1194 | switch (try operand_bind.resolveToMcv(self)) { | |
| 1190 | 1195 | .dead => unreachable, |
| 1191 | 1196 | .unreach => unreachable, |
| 1192 | 1197 | .cpsr_flags => |cond| break :result MCValue{ .cpsr_flags = cond.negate() }, |
| 1193 | 1198 | else => { |
| 1194 | 1199 | switch (operand_ty.zigTypeTag()) { |
| 1195 | 1200 | .Bool => { |
| 1196 | const op_reg = switch (operand) { | |
| 1197 | .register => |r| r, | |
| 1198 | else => try self.copyToTmpRegister(operand_ty, operand), | |
| 1199 | }; | |
| 1200 | const op_reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); | |
| 1201 | defer self.register_manager.unlockReg(op_reg_lock); | |
| 1201 | var op_reg: Register = undefined; | |
| 1202 | var dest_reg: Register = undefined; | |
| 1202 | 1203 | |
| 1203 | const dest_reg = blk: { | |
| 1204 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { | |
| 1205 | break :blk op_reg; | |
| 1206 | } | |
| 1207 | ||
| 1208 | break :blk try self.register_manager.allocReg(null, gp); | |
| 1204 | const read_args = [_]ReadArg{ | |
| 1205 | .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg }, | |
| 1209 | 1206 | }; |
| 1207 | const write_args = [_]WriteArg{ | |
| 1208 | .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 1209 | }; | |
| 1210 | try self.allocRegs( | |
| 1211 | &read_args, | |
| 1212 | &write_args, | |
| 1213 | ReuseMetadata{ | |
| 1214 | .corresponding_inst = inst, | |
| 1215 | .operand_mapping = &.{0}, | |
| 1216 | }, | |
| 1217 | ); | |
| 1210 | 1218 | |
| 1211 | 1219 | _ = try self.addInst(.{ |
| 1212 | 1220 | .tag = .eor, |
| ... | ... | @@ -1223,26 +1231,28 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1223 | 1231 | .Int => { |
| 1224 | 1232 | const int_info = operand_ty.intInfo(self.target.*); |
| 1225 | 1233 | if (int_info.bits <= 32) { |
| 1226 | const op_reg = switch (operand) { | |
| 1227 | .register => |r| r, | |
| 1228 | else => try self.copyToTmpRegister(operand_ty, operand), | |
| 1229 | }; | |
| 1230 | const op_reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); | |
| 1231 | defer self.register_manager.unlockReg(op_reg_lock); | |
| 1232 | ||
| 1233 | const dest_reg = blk: { | |
| 1234 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { | |
| 1235 | break :blk op_reg; | |
| 1236 | } | |
| 1234 | var op_reg: Register = undefined; | |
| 1235 | var dest_reg: Register = undefined; | |
| 1237 | 1236 | |
| 1238 | break :blk try self.register_manager.allocReg(null, gp); | |
| 1237 | const read_args = [_]ReadArg{ | |
| 1238 | .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg }, | |
| 1239 | 1239 | }; |
| 1240 | const write_args = [_]WriteArg{ | |
| 1241 | .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 1242 | }; | |
| 1243 | try self.allocRegs( | |
| 1244 | &read_args, | |
| 1245 | &write_args, | |
| 1246 | ReuseMetadata{ | |
| 1247 | .corresponding_inst = inst, | |
| 1248 | .operand_mapping = &.{0}, | |
| 1249 | }, | |
| 1250 | ); | |
| 1240 | 1251 | |
| 1241 | 1252 | _ = try self.addInst(.{ |
| 1242 | 1253 | .tag = .mvn, |
| 1243 | .data = .{ .rr_op = .{ | |
| 1254 | .data = .{ .r_op_mov = .{ | |
| 1244 | 1255 | .rd = dest_reg, |
| 1245 | .rn = undefined, | |
| 1246 | 1256 | .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none), |
| 1247 | 1257 | } }, |
| 1248 | 1258 | }); |
| ... | ... | @@ -1267,11 +1277,11 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1267 | 1277 | fn minMax( |
| 1268 | 1278 | self: *Self, |
| 1269 | 1279 | tag: Air.Inst.Tag, |
| 1270 | maybe_inst: ?Air.Inst.Index, | |
| 1271 | lhs: MCValue, | |
| 1272 | rhs: MCValue, | |
| 1280 | lhs_bind: ReadArg.Bind, | |
| 1281 | rhs_bind: ReadArg.Bind, | |
| 1273 | 1282 | lhs_ty: Type, |
| 1274 | 1283 | rhs_ty: Type, |
| 1284 | maybe_inst: ?Air.Inst.Index, | |
| 1275 | 1285 | ) !MCValue { |
| 1276 | 1286 | switch (lhs_ty.zigTypeTag()) { |
| 1277 | 1287 | .Float => return self.fail("TODO ARM min/max on floats", .{}), |
| ... | ... | @@ -1281,34 +1291,25 @@ fn minMax( |
| 1281 | 1291 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1282 | 1292 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1283 | 1293 | if (int_info.bits <= 32) { |
| 1284 | const lhs_is_register = lhs == .register; | |
| 1285 | const rhs_is_register = rhs == .register; | |
| 1294 | var lhs_reg: Register = undefined; | |
| 1295 | var rhs_reg: Register = undefined; | |
| 1296 | var dest_reg: Register = undefined; | |
| 1286 | 1297 | |
| 1287 | const lhs_reg = switch (lhs) { | |
| 1288 | .register => |r| r, | |
| 1289 | else => try self.copyToTmpRegister(lhs_ty, lhs), | |
| 1298 | const read_args = [_]ReadArg{ | |
| 1299 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, | |
| 1300 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, | |
| 1290 | 1301 | }; |
| 1291 | const lhs_reg_lock = self.register_manager.lockReg(lhs_reg); | |
| 1292 | defer if (lhs_reg_lock) |reg| self.register_manager.unlockReg(reg); | |
| 1293 | ||
| 1294 | const rhs_reg = switch (rhs) { | |
| 1295 | .register => |r| r, | |
| 1296 | else => try self.copyToTmpRegister(rhs_ty, rhs), | |
| 1302 | const write_args = [_]WriteArg{ | |
| 1303 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 1297 | 1304 | }; |
| 1298 | const rhs_reg_lock = self.register_manager.lockReg(rhs_reg); | |
| 1299 | defer if (rhs_reg_lock) |reg| self.register_manager.unlockReg(reg); | |
| 1300 | ||
| 1301 | const dest_reg = if (maybe_inst) |inst| blk: { | |
| 1302 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1303 | ||
| 1304 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { | |
| 1305 | break :blk lhs_reg; | |
| 1306 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { | |
| 1307 | break :blk rhs_reg; | |
| 1308 | } else { | |
| 1309 | break :blk try self.register_manager.allocReg(inst, gp); | |
| 1310 | } | |
| 1311 | } else try self.register_manager.allocReg(null, gp); | |
| 1305 | try self.allocRegs( | |
| 1306 | &read_args, | |
| 1307 | &write_args, | |
| 1308 | if (maybe_inst) |inst| .{ | |
| 1309 | .corresponding_inst = inst, | |
| 1310 | .operand_mapping = &.{ 0, 1 }, | |
| 1311 | } else null, | |
| 1312 | ); | |
| 1312 | 1313 | |
| 1313 | 1314 | // lhs == reg should have been checked by airMinMax |
| 1314 | 1315 | // |
| ... | ... | @@ -1318,7 +1319,13 @@ fn minMax( |
| 1318 | 1319 | // register. |
| 1319 | 1320 | assert(lhs_reg != rhs_reg); // see note above |
| 1320 | 1321 | |
| 1321 | _ = try self.binOpRegister(.cmp, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty, null); | |
| 1322 | _ = try self.addInst(.{ | |
| 1323 | .tag = .cmp, | |
| 1324 | .data = .{ .r_op_cmp = .{ | |
| 1325 | .rn = lhs_reg, | |
| 1326 | .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), | |
| 1327 | } }, | |
| 1328 | }); | |
| 1322 | 1329 | |
| 1323 | 1330 | const cond_choose_lhs: Condition = switch (tag) { |
| 1324 | 1331 | .max => switch (int_info.signedness) { |
| ... | ... | @@ -1337,9 +1344,8 @@ fn minMax( |
| 1337 | 1344 | _ = try self.addInst(.{ |
| 1338 | 1345 | .tag = .mov, |
| 1339 | 1346 | .cond = cond_choose_lhs, |
| 1340 | .data = .{ .rr_op = .{ | |
| 1347 | .data = .{ .r_op_mov = .{ | |
| 1341 | 1348 | .rd = dest_reg, |
| 1342 | .rn = .r0, | |
| 1343 | 1349 | .op = Instruction.Operand.reg(lhs_reg, Instruction.Operand.Shift.none), |
| 1344 | 1350 | } }, |
| 1345 | 1351 | }); |
| ... | ... | @@ -1348,9 +1354,8 @@ fn minMax( |
| 1348 | 1354 | _ = try self.addInst(.{ |
| 1349 | 1355 | .tag = .mov, |
| 1350 | 1356 | .cond = cond_choose_rhs, |
| 1351 | .data = .{ .rr_op = .{ | |
| 1357 | .data = .{ .r_op_mov = .{ | |
| 1352 | 1358 | .rd = dest_reg, |
| 1353 | .rn = .r0, | |
| 1354 | 1359 | .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), |
| 1355 | 1360 | } }, |
| 1356 | 1361 | }); |
| ... | ... | @@ -1368,15 +1373,17 @@ fn minMax( |
| 1368 | 1373 | fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { |
| 1369 | 1374 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1370 | 1375 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1371 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 1372 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 1373 | 1376 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1374 | 1377 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1375 | 1378 | |
| 1376 | 1379 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1380 | const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; | |
| 1381 | const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; | |
| 1382 | ||
| 1383 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 1377 | 1384 | if (bin_op.lhs == bin_op.rhs) break :result lhs; |
| 1378 | 1385 | |
| 1379 | break :result try self.minMax(tag, inst, lhs, rhs, lhs_ty, rhs_ty); | |
| 1386 | break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); | |
| 1380 | 1387 | }; |
| 1381 | 1388 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1382 | 1389 | } |
| ... | ... | @@ -1390,7 +1397,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1390 | 1397 | const len = try self.resolveInst(bin_op.rhs); |
| 1391 | 1398 | const len_ty = self.air.typeOf(bin_op.rhs); |
| 1392 | 1399 | |
| 1393 | const stack_offset = try self.allocMem(inst, 8, 4); | |
| 1400 | const stack_offset = try self.allocMem(8, 4, inst); | |
| 1394 | 1401 | try self.genSetStack(ptr_ty, stack_offset, ptr); |
| 1395 | 1402 | try self.genSetStack(len_ty, stack_offset - 4, len); |
| 1396 | 1403 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -1400,38 +1407,65 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1400 | 1407 | |
| 1401 | 1408 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 1402 | 1409 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1403 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 1404 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 1405 | 1410 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1406 | 1411 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1407 | 1412 | |
| 1408 | const result: MCValue = if (self.liveness.isUnused(inst)) | |
| 1409 | .dead | |
| 1410 | else | |
| 1411 | try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ | |
| 1412 | .lhs = bin_op.lhs, | |
| 1413 | .rhs = bin_op.rhs, | |
| 1414 | .inst = inst, | |
| 1415 | }); | |
| 1413 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | |
| 1414 | const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; | |
| 1415 | const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; | |
| 1416 | ||
| 1417 | break :result switch (tag) { | |
| 1418 | .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1419 | .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1420 | ||
| 1421 | .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1422 | ||
| 1423 | .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1424 | ||
| 1425 | .div_trunc => try self.div(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1426 | .div_floor => try self.div(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1427 | ||
| 1428 | .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1429 | ||
| 1430 | .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1431 | ||
| 1432 | .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1433 | ||
| 1434 | .addwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1435 | .subwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1436 | .mulwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1437 | ||
| 1438 | .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1439 | .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1440 | .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1441 | ||
| 1442 | .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1443 | .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1444 | ||
| 1445 | .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1446 | .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1447 | ||
| 1448 | .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1449 | .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), | |
| 1450 | ||
| 1451 | else => unreachable, | |
| 1452 | }; | |
| 1453 | }; | |
| 1416 | 1454 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1417 | 1455 | } |
| 1418 | 1456 | |
| 1419 | 1457 | fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 1420 | 1458 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1421 | 1459 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1422 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 1423 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 1424 | 1460 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1425 | 1461 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1426 | 1462 | |
| 1427 | const result: MCValue = if (self.liveness.isUnused(inst)) | |
| 1428 | .dead | |
| 1429 | else | |
| 1430 | try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ | |
| 1431 | .lhs = bin_op.lhs, | |
| 1432 | .rhs = bin_op.rhs, | |
| 1433 | .inst = inst, | |
| 1434 | }); | |
| 1463 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | |
| 1464 | const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; | |
| 1465 | const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; | |
| 1466 | ||
| 1467 | break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); | |
| 1468 | }; | |
| 1435 | 1469 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1436 | 1470 | } |
| 1437 | 1471 | |
| ... | ... | @@ -1458,8 +1492,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1458 | 1492 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1459 | 1493 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1460 | 1494 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1461 | const lhs = try self.resolveInst(extra.lhs); | |
| 1462 | const rhs = try self.resolveInst(extra.rhs); | |
| 1495 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; | |
| 1496 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; | |
| 1463 | 1497 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 1464 | 1498 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 1465 | 1499 | |
| ... | ... | @@ -1475,17 +1509,16 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1475 | 1509 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1476 | 1510 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1477 | 1511 | if (int_info.bits < 32) { |
| 1478 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); | |
| 1512 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); | |
| 1479 | 1513 | |
| 1480 | 1514 | try self.spillCompareFlagsIfOccupied(); |
| 1481 | self.cpsr_flags_inst = null; | |
| 1482 | 1515 | |
| 1483 | 1516 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 1484 | 1517 | .add_with_overflow => .add, |
| 1485 | 1518 | .sub_with_overflow => .sub, |
| 1486 | 1519 | else => unreachable, |
| 1487 | 1520 | }; |
| 1488 | const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); | |
| 1521 | const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); | |
| 1489 | 1522 | const dest_reg = dest.register; |
| 1490 | 1523 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 1491 | 1524 | defer self.register_manager.unlockReg(dest_reg_lock); |
| ... | ... | @@ -1498,25 +1531,34 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1498 | 1531 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 1499 | 1532 | |
| 1500 | 1533 | // cmp dest, truncated |
| 1501 | _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); | |
| 1534 | _ = try self.addInst(.{ | |
| 1535 | .tag = .cmp, | |
| 1536 | .data = .{ .r_op_cmp = .{ | |
| 1537 | .rn = dest_reg, | |
| 1538 | .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none), | |
| 1539 | } }, | |
| 1540 | }); | |
| 1502 | 1541 | |
| 1503 | 1542 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1504 | 1543 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne }); |
| 1505 | 1544 | |
| 1506 | 1545 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1507 | 1546 | } else if (int_info.bits == 32) { |
| 1547 | const lhs_immediate = try lhs_bind.resolveToImmediate(self); | |
| 1548 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); | |
| 1549 | ||
| 1508 | 1550 | // Only say yes if the operation is |
| 1509 | 1551 | // commutative, i.e. we can swap both of the |
| 1510 | 1552 | // operands |
| 1511 | 1553 | const lhs_immediate_ok = switch (tag) { |
| 1512 | .add_with_overflow => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null, | |
| 1554 | .add_with_overflow => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false, | |
| 1513 | 1555 | .sub_with_overflow => false, |
| 1514 | 1556 | else => unreachable, |
| 1515 | 1557 | }; |
| 1516 | 1558 | const rhs_immediate_ok = switch (tag) { |
| 1517 | 1559 | .add_with_overflow, |
| 1518 | 1560 | .sub_with_overflow, |
| 1519 | => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null, | |
| 1561 | => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false, | |
| 1520 | 1562 | else => unreachable, |
| 1521 | 1563 | }; |
| 1522 | 1564 | |
| ... | ... | @@ -1531,12 +1573,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1531 | 1573 | |
| 1532 | 1574 | const dest = blk: { |
| 1533 | 1575 | if (rhs_immediate_ok) { |
| 1534 | break :blk try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, null); | |
| 1576 | break :blk try self.binOpImmediateNew(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, null); | |
| 1535 | 1577 | } else if (lhs_immediate_ok) { |
| 1536 | 1578 | // swap lhs and rhs |
| 1537 | break :blk try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, null); | |
| 1579 | break :blk try self.binOpImmediateNew(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, null); | |
| 1538 | 1580 | } else { |
| 1539 | break :blk try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, null); | |
| 1581 | break :blk try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); | |
| 1540 | 1582 | } |
| 1541 | 1583 | }; |
| 1542 | 1584 | |
| ... | ... | @@ -1563,8 +1605,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1563 | 1605 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1564 | 1606 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 1565 | 1607 | const result: MCValue = result: { |
| 1566 | const lhs = try self.resolveInst(extra.lhs); | |
| 1567 | const rhs = try self.resolveInst(extra.rhs); | |
| 1608 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; | |
| 1609 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; | |
| 1568 | 1610 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 1569 | 1611 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 1570 | 1612 | |
| ... | ... | @@ -1580,17 +1622,16 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1580 | 1622 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1581 | 1623 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1582 | 1624 | if (int_info.bits <= 16) { |
| 1583 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); | |
| 1625 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); | |
| 1584 | 1626 | |
| 1585 | 1627 | try self.spillCompareFlagsIfOccupied(); |
| 1586 | self.cpsr_flags_inst = null; | |
| 1587 | 1628 | |
| 1588 | 1629 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { |
| 1589 | 1630 | .signed => .smulbb, |
| 1590 | 1631 | .unsigned => .mul, |
| 1591 | 1632 | }; |
| 1592 | 1633 | |
| 1593 | const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); | |
| 1634 | const dest = try self.binOpRegisterNew(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); | |
| 1594 | 1635 | const dest_reg = dest.register; |
| 1595 | 1636 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 1596 | 1637 | defer self.register_manager.unlockReg(dest_reg_lock); |
| ... | ... | @@ -1603,62 +1644,48 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1603 | 1644 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 1604 | 1645 | |
| 1605 | 1646 | // cmp dest, truncated |
| 1606 | _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); | |
| 1647 | _ = try self.addInst(.{ | |
| 1648 | .tag = .cmp, | |
| 1649 | .data = .{ .r_op_cmp = .{ | |
| 1650 | .rn = dest_reg, | |
| 1651 | .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none), | |
| 1652 | } }, | |
| 1653 | }); | |
| 1607 | 1654 | |
| 1608 | 1655 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1609 | 1656 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne }); |
| 1610 | 1657 | |
| 1611 | 1658 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1612 | 1659 | } else if (int_info.bits <= 32) { |
| 1613 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); | |
| 1660 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); | |
| 1614 | 1661 | |
| 1615 | 1662 | try self.spillCompareFlagsIfOccupied(); |
| 1616 | self.cpsr_flags_inst = null; | |
| 1617 | 1663 | |
| 1618 | 1664 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { |
| 1619 | 1665 | .signed => .smull, |
| 1620 | 1666 | .unsigned => .umull, |
| 1621 | 1667 | }; |
| 1622 | 1668 | |
| 1623 | // TODO extract umull etc. to binOpTwoRegister | |
| 1624 | // once MCValue.rr is implemented | |
| 1625 | const lhs_is_register = lhs == .register; | |
| 1626 | const rhs_is_register = rhs == .register; | |
| 1627 | ||
| 1628 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | |
| 1629 | self.register_manager.lockReg(lhs.register) | |
| 1630 | else | |
| 1631 | null; | |
| 1632 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 1633 | ||
| 1634 | const lhs_reg = if (lhs_is_register) | |
| 1635 | lhs.register | |
| 1636 | else | |
| 1637 | try self.register_manager.allocReg(null, gp); | |
| 1638 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | |
| 1639 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 1640 | ||
| 1641 | const rhs_reg = if (rhs_is_register) | |
| 1642 | rhs.register | |
| 1643 | else | |
| 1644 | try self.register_manager.allocReg(null, gp); | |
| 1645 | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); | |
| 1646 | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 1647 | ||
| 1648 | const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); | |
| 1649 | const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs); | |
| 1650 | defer for (dest_regs_locks) |reg| { | |
| 1651 | self.register_manager.unlockReg(reg); | |
| 1652 | }; | |
| 1653 | const rdlo = dest_regs[0]; | |
| 1654 | const rdhi = dest_regs[1]; | |
| 1655 | ||
| 1656 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | |
| 1657 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | |
| 1669 | var lhs_reg: Register = undefined; | |
| 1670 | var rhs_reg: Register = undefined; | |
| 1671 | var rdhi: Register = undefined; | |
| 1672 | var rdlo: Register = undefined; | |
| 1673 | var truncated_reg: Register = undefined; | |
| 1658 | 1674 | |
| 1659 | const truncated_reg = try self.register_manager.allocReg(null, gp); | |
| 1660 | const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); | |
| 1661 | defer self.register_manager.unlockReg(truncated_reg_lock); | |
| 1675 | const read_args = [_]ReadArg{ | |
| 1676 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, | |
| 1677 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, | |
| 1678 | }; | |
| 1679 | const write_args = [_]WriteArg{ | |
| 1680 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdhi }, | |
| 1681 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdlo }, | |
| 1682 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg }, | |
| 1683 | }; | |
| 1684 | try self.allocRegs( | |
| 1685 | &read_args, | |
| 1686 | &write_args, | |
| 1687 | null, | |
| 1688 | ); | |
| 1662 | 1689 | |
| 1663 | 1690 | _ = try self.addInst(.{ |
| 1664 | 1691 | .tag = base_tag, |
| ... | ... | @@ -1677,14 +1704,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1677 | 1704 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1678 | 1705 | |
| 1679 | 1706 | // cmp truncated, rdlo |
| 1680 | _ = try self.binOp(.cmp_eq, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize, null); | |
| 1707 | _ = try self.addInst(.{ | |
| 1708 | .tag = .cmp, | |
| 1709 | .data = .{ .r_op_cmp = .{ | |
| 1710 | .rn = truncated_reg, | |
| 1711 | .op = Instruction.Operand.reg(rdlo, Instruction.Operand.Shift.none), | |
| 1712 | } }, | |
| 1713 | }); | |
| 1681 | 1714 | |
| 1682 | 1715 | // mov rdlo, #0 |
| 1683 | 1716 | _ = try self.addInst(.{ |
| 1684 | 1717 | .tag = .mov, |
| 1685 | .data = .{ .rr_op = .{ | |
| 1718 | .data = .{ .r_op_mov = .{ | |
| 1686 | 1719 | .rd = rdlo, |
| 1687 | .rn = .r0, | |
| 1688 | 1720 | .op = Instruction.Operand.fromU32(0).?, |
| 1689 | 1721 | } }, |
| 1690 | 1722 | }); |
| ... | ... | @@ -1693,23 +1725,27 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1693 | 1725 | _ = try self.addInst(.{ |
| 1694 | 1726 | .tag = .mov, |
| 1695 | 1727 | .cond = .ne, |
| 1696 | .data = .{ .rr_op = .{ | |
| 1728 | .data = .{ .r_op_mov = .{ | |
| 1697 | 1729 | .rd = rdlo, |
| 1698 | .rn = .r0, | |
| 1699 | 1730 | .op = Instruction.Operand.fromU32(1).?, |
| 1700 | 1731 | } }, |
| 1701 | 1732 | }); |
| 1702 | 1733 | |
| 1703 | 1734 | // cmp rdhi, #0 |
| 1704 | _ = try self.binOp(.cmp_eq, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize, null); | |
| 1735 | _ = try self.addInst(.{ | |
| 1736 | .tag = .cmp, | |
| 1737 | .data = .{ .r_op_cmp = .{ | |
| 1738 | .rn = rdhi, | |
| 1739 | .op = Instruction.Operand.fromU32(0).?, | |
| 1740 | } }, | |
| 1741 | }); | |
| 1705 | 1742 | |
| 1706 | 1743 | // movne rdlo, #1 |
| 1707 | 1744 | _ = try self.addInst(.{ |
| 1708 | 1745 | .tag = .mov, |
| 1709 | 1746 | .cond = .ne, |
| 1710 | .data = .{ .rr_op = .{ | |
| 1747 | .data = .{ .r_op_mov = .{ | |
| 1711 | 1748 | .rd = rdlo, |
| 1712 | .rn = .r0, | |
| 1713 | 1749 | .op = Instruction.Operand.fromU32(1).?, |
| 1714 | 1750 | } }, |
| 1715 | 1751 | }); |
| ... | ... | @@ -1733,8 +1769,6 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1733 | 1769 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1734 | 1770 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 1735 | 1771 | const result: MCValue = result: { |
| 1736 | const lhs = try self.resolveInst(extra.lhs); | |
| 1737 | const rhs = try self.resolveInst(extra.rhs); | |
| 1738 | 1772 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 1739 | 1773 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 1740 | 1774 | |
| ... | ... | @@ -1748,30 +1782,109 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1748 | 1782 | .Int => { |
| 1749 | 1783 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1750 | 1784 | if (int_info.bits <= 32) { |
| 1751 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); | |
| 1752 | ||
| 1753 | const lhs_lock: ?RegisterLock = if (lhs == .register) | |
| 1754 | self.register_manager.lockRegAssumeUnused(lhs.register) | |
| 1755 | else | |
| 1756 | null; | |
| 1757 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 1785 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); | |
| 1758 | 1786 | |
| 1759 | 1787 | try self.spillCompareFlagsIfOccupied(); |
| 1760 | self.cpsr_flags_inst = null; | |
| 1761 | 1788 | |
| 1762 | // lsl dest, lhs, rhs | |
| 1763 | const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); | |
| 1764 | const dest_reg = dest.register; | |
| 1765 | const dest_lock = self.register_manager.lockRegAssumeUnused(dest_reg); | |
| 1766 | defer self.register_manager.unlockReg(dest_lock); | |
| 1789 | const shr_mir_tag: Mir.Inst.Tag = switch (int_info.signedness) { | |
| 1790 | .signed => Mir.Inst.Tag.asr, | |
| 1791 | .unsigned => Mir.Inst.Tag.lsr, | |
| 1792 | }; | |
| 1793 | ||
| 1794 | var lhs_reg: Register = undefined; | |
| 1795 | var rhs_reg: Register = undefined; | |
| 1796 | var dest_reg: Register = undefined; | |
| 1797 | var reconstructed_reg: Register = undefined; | |
| 1798 | ||
| 1799 | const rhs_mcv = try self.resolveInst(extra.rhs); | |
| 1800 | const rhs_immediate_ok = rhs_mcv == .immediate and Instruction.Operand.fromU32(rhs_mcv.immediate) != null; | |
| 1801 | ||
| 1802 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; | |
| 1803 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; | |
| 1804 | ||
| 1805 | if (rhs_immediate_ok) { | |
| 1806 | const read_args = [_]ReadArg{ | |
| 1807 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, | |
| 1808 | }; | |
| 1809 | const write_args = [_]WriteArg{ | |
| 1810 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 1811 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, | |
| 1812 | }; | |
| 1813 | try self.allocRegs( | |
| 1814 | &read_args, | |
| 1815 | &write_args, | |
| 1816 | null, | |
| 1817 | ); | |
| 1818 | ||
| 1819 | // lsl dest, lhs, rhs | |
| 1820 | _ = try self.addInst(.{ | |
| 1821 | .tag = .lsl, | |
| 1822 | .data = .{ .rr_shift = .{ | |
| 1823 | .rd = dest_reg, | |
| 1824 | .rm = lhs_reg, | |
| 1825 | .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs_mcv.immediate)), | |
| 1826 | } }, | |
| 1827 | }); | |
| 1828 | ||
| 1829 | try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); | |
| 1830 | ||
| 1831 | // asr/lsr reconstructed, dest, rhs | |
| 1832 | _ = try self.addInst(.{ | |
| 1833 | .tag = shr_mir_tag, | |
| 1834 | .data = .{ .rr_shift = .{ | |
| 1835 | .rd = reconstructed_reg, | |
| 1836 | .rm = dest_reg, | |
| 1837 | .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs_mcv.immediate)), | |
| 1838 | } }, | |
| 1839 | }); | |
| 1840 | } else { | |
| 1841 | const read_args = [_]ReadArg{ | |
| 1842 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, | |
| 1843 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, | |
| 1844 | }; | |
| 1845 | const write_args = [_]WriteArg{ | |
| 1846 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 1847 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, | |
| 1848 | }; | |
| 1849 | try self.allocRegs( | |
| 1850 | &read_args, | |
| 1851 | &write_args, | |
| 1852 | null, | |
| 1853 | ); | |
| 1854 | ||
| 1855 | // lsl dest, lhs, rhs | |
| 1856 | _ = try self.addInst(.{ | |
| 1857 | .tag = .lsl, | |
| 1858 | .data = .{ .rr_shift = .{ | |
| 1859 | .rd = dest_reg, | |
| 1860 | .rm = lhs_reg, | |
| 1861 | .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), | |
| 1862 | } }, | |
| 1863 | }); | |
| 1864 | ||
| 1865 | try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); | |
| 1767 | 1866 | |
| 1768 | // asr/lsr reconstructed, dest, rhs | |
| 1769 | const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null); | |
| 1867 | // asr/lsr reconstructed, dest, rhs | |
| 1868 | _ = try self.addInst(.{ | |
| 1869 | .tag = shr_mir_tag, | |
| 1870 | .data = .{ .rr_shift = .{ | |
| 1871 | .rd = reconstructed_reg, | |
| 1872 | .rm = dest_reg, | |
| 1873 | .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), | |
| 1874 | } }, | |
| 1875 | }); | |
| 1876 | } | |
| 1770 | 1877 | |
| 1771 | 1878 | // cmp lhs, reconstructed |
| 1772 | _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null); | |
| 1879 | _ = try self.addInst(.{ | |
| 1880 | .tag = .cmp, | |
| 1881 | .data = .{ .r_op_cmp = .{ | |
| 1882 | .rn = lhs_reg, | |
| 1883 | .op = Instruction.Operand.reg(reconstructed_reg, Instruction.Operand.Shift.none), | |
| 1884 | } }, | |
| 1885 | }); | |
| 1773 | 1886 | |
| 1774 | try self.genSetStack(lhs_ty, stack_offset, dest); | |
| 1887 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg }); | |
| 1775 | 1888 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne }); |
| 1776 | 1889 | |
| 1777 | 1890 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -1826,19 +1939,57 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 1826 | 1939 | } |
| 1827 | 1940 | |
| 1828 | 1941 | /// Given an error union, returns the error |
| 1829 | fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { | |
| 1942 | fn errUnionErr( | |
| 1943 | self: *Self, | |
| 1944 | error_union_bind: ReadArg.Bind, | |
| 1945 | error_union_ty: Type, | |
| 1946 | maybe_inst: ?Air.Inst.Index, | |
| 1947 | ) !MCValue { | |
| 1830 | 1948 | const err_ty = error_union_ty.errorUnionSet(); |
| 1831 | 1949 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 1832 | 1950 | if (err_ty.errorSetIsEmpty()) { |
| 1833 | 1951 | return MCValue{ .immediate = 0 }; |
| 1834 | 1952 | } |
| 1835 | 1953 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 1836 | return error_union_mcv; | |
| 1954 | return try error_union_bind.resolveToMcv(self); | |
| 1837 | 1955 | } |
| 1838 | 1956 | |
| 1839 | 1957 | const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); |
| 1840 | switch (error_union_mcv) { | |
| 1841 | .register => return self.fail("TODO errUnionErr for registers", .{}), | |
| 1958 | switch (try error_union_bind.resolveToMcv(self)) { | |
| 1959 | .register => { | |
| 1960 | var operand_reg: Register = undefined; | |
| 1961 | var dest_reg: Register = undefined; | |
| 1962 | ||
| 1963 | const read_args = [_]ReadArg{ | |
| 1964 | .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, | |
| 1965 | }; | |
| 1966 | const write_args = [_]WriteArg{ | |
| 1967 | .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 1968 | }; | |
| 1969 | try self.allocRegs( | |
| 1970 | &read_args, | |
| 1971 | &write_args, | |
| 1972 | if (maybe_inst) |inst| .{ | |
| 1973 | .corresponding_inst = inst, | |
| 1974 | .operand_mapping = &.{0}, | |
| 1975 | } else null, | |
| 1976 | ); | |
| 1977 | ||
| 1978 | const err_bit_offset = err_offset * 8; | |
| 1979 | const err_bit_size = @intCast(u32, err_ty.abiSize(self.target.*)) * 8; | |
| 1980 | ||
| 1981 | _ = try self.addInst(.{ | |
| 1982 | .tag = .ubfx, // errors are unsigned integers | |
| 1983 | .data = .{ .rr_lsb_width = .{ | |
| 1984 | .rd = dest_reg, | |
| 1985 | .rn = operand_reg, | |
| 1986 | .lsb = @intCast(u5, err_bit_offset), | |
| 1987 | .width = @intCast(u6, err_bit_size), | |
| 1988 | } }, | |
| 1989 | }); | |
| 1990 | ||
| 1991 | return MCValue{ .register = dest_reg }; | |
| 1992 | }, | |
| 1842 | 1993 | .stack_argument_offset => |off| { |
| 1843 | 1994 | return MCValue{ .stack_argument_offset = off + err_offset }; |
| 1844 | 1995 | }, |
| ... | ... | @@ -1855,27 +2006,66 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV |
| 1855 | 2006 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1856 | 2007 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1857 | 2008 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2009 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; | |
| 1858 | 2010 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1859 | const mcv = try self.resolveInst(ty_op.operand); | |
| 1860 | break :result try self.errUnionErr(mcv, error_union_ty); | |
| 2011 | ||
| 2012 | break :result try self.errUnionErr(error_union_bind, error_union_ty, inst); | |
| 1861 | 2013 | }; |
| 1862 | 2014 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1863 | 2015 | } |
| 1864 | 2016 | |
| 1865 | 2017 | /// Given an error union, returns the payload |
| 1866 | fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { | |
| 2018 | fn errUnionPayload( | |
| 2019 | self: *Self, | |
| 2020 | error_union_bind: ReadArg.Bind, | |
| 2021 | error_union_ty: Type, | |
| 2022 | maybe_inst: ?Air.Inst.Index, | |
| 2023 | ) !MCValue { | |
| 1867 | 2024 | const err_ty = error_union_ty.errorUnionSet(); |
| 1868 | 2025 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 1869 | 2026 | if (err_ty.errorSetIsEmpty()) { |
| 1870 | return error_union_mcv; | |
| 2027 | return try error_union_bind.resolveToMcv(self); | |
| 1871 | 2028 | } |
| 1872 | 2029 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 1873 | 2030 | return MCValue.none; |
| 1874 | 2031 | } |
| 1875 | 2032 | |
| 1876 | 2033 | const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); |
| 1877 | switch (error_union_mcv) { | |
| 1878 | .register => return self.fail("TODO errUnionPayload for registers", .{}), | |
| 2034 | switch (try error_union_bind.resolveToMcv(self)) { | |
| 2035 | .register => { | |
| 2036 | var operand_reg: Register = undefined; | |
| 2037 | var dest_reg: Register = undefined; | |
| 2038 | ||
| 2039 | const read_args = [_]ReadArg{ | |
| 2040 | .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, | |
| 2041 | }; | |
| 2042 | const write_args = [_]WriteArg{ | |
| 2043 | .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 2044 | }; | |
| 2045 | try self.allocRegs( | |
| 2046 | &read_args, | |
| 2047 | &write_args, | |
| 2048 | if (maybe_inst) |inst| .{ | |
| 2049 | .corresponding_inst = inst, | |
| 2050 | .operand_mapping = &.{0}, | |
| 2051 | } else null, | |
| 2052 | ); | |
| 2053 | ||
| 2054 | const payload_bit_offset = payload_offset * 8; | |
| 2055 | const payload_bit_size = @intCast(u32, payload_ty.abiSize(self.target.*)) * 8; | |
| 2056 | ||
| 2057 | _ = try self.addInst(.{ | |
| 2058 | .tag = if (payload_ty.isSignedInt()) Mir.Inst.Tag.sbfx else .ubfx, | |
| 2059 | .data = .{ .rr_lsb_width = .{ | |
| 2060 | .rd = dest_reg, | |
| 2061 | .rn = operand_reg, | |
| 2062 | .lsb = @intCast(u5, payload_bit_offset), | |
| 2063 | .width = @intCast(u6, payload_bit_size), | |
| 2064 | } }, | |
| 2065 | }); | |
| 2066 | ||
| 2067 | return MCValue{ .register = dest_reg }; | |
| 2068 | }, | |
| 1879 | 2069 | .stack_argument_offset => |off| { |
| 1880 | 2070 | return MCValue{ .stack_argument_offset = off + payload_offset }; |
| 1881 | 2071 | }, |
| ... | ... | @@ -1892,9 +2082,10 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) |
| 1892 | 2082 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1893 | 2083 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1894 | 2084 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2085 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; | |
| 1895 | 2086 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1896 | const error_union = try self.resolveInst(ty_op.operand); | |
| 1897 | break :result try self.errUnionPayload(error_union, error_union_ty); | |
| 2087 | ||
| 2088 | break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst); | |
| 1898 | 2089 | }; |
| 1899 | 2090 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1900 | 2091 | } |
| ... | ... | @@ -1938,17 +2129,18 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1938 | 2129 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1939 | 2130 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1940 | 2131 | const error_union_ty = self.air.getRefType(ty_op.ty); |
| 2132 | const error_ty = error_union_ty.errorUnionSet(); | |
| 1941 | 2133 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 1942 | 2134 | const operand = try self.resolveInst(ty_op.operand); |
| 1943 | 2135 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand; |
| 1944 | 2136 | |
| 1945 | 2137 | const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); |
| 1946 | 2138 | const abi_align = error_union_ty.abiAlignment(self.target.*); |
| 1947 | const stack_offset = @intCast(u32, try self.allocMem(inst, abi_size, abi_align)); | |
| 2139 | const stack_offset = @intCast(u32, try self.allocMem(abi_size, abi_align, inst)); | |
| 1948 | 2140 | const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); |
| 1949 | 2141 | const err_off = errUnionErrorOffset(payload_ty, self.target.*); |
| 1950 | 2142 | try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), operand); |
| 1951 | try self.genSetStack(Type.anyerror, stack_offset - @intCast(u32, err_off), .{ .immediate = 0 }); | |
| 2143 | try self.genSetStack(error_ty, stack_offset - @intCast(u32, err_off), .{ .immediate = 0 }); | |
| 1952 | 2144 | |
| 1953 | 2145 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1954 | 2146 | }; |
| ... | ... | @@ -1960,16 +2152,17 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1960 | 2152 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1961 | 2153 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1962 | 2154 | const error_union_ty = self.air.getRefType(ty_op.ty); |
| 2155 | const error_ty = error_union_ty.errorUnionSet(); | |
| 1963 | 2156 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 1964 | 2157 | const operand = try self.resolveInst(ty_op.operand); |
| 1965 | 2158 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand; |
| 1966 | 2159 | |
| 1967 | 2160 | const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); |
| 1968 | 2161 | const abi_align = error_union_ty.abiAlignment(self.target.*); |
| 1969 | const stack_offset = @intCast(u32, try self.allocMem(inst, abi_size, abi_align)); | |
| 2162 | const stack_offset = @intCast(u32, try self.allocMem(abi_size, abi_align, inst)); | |
| 1970 | 2163 | const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); |
| 1971 | 2164 | const err_off = errUnionErrorOffset(payload_ty, self.target.*); |
| 1972 | try self.genSetStack(Type.anyerror, stack_offset - @intCast(u32, err_off), operand); | |
| 2165 | try self.genSetStack(error_ty, stack_offset - @intCast(u32, err_off), operand); | |
| 1973 | 2166 | try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), .undef); |
| 1974 | 2167 | |
| 1975 | 2168 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -2008,7 +2201,6 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 2008 | 2201 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2009 | 2202 | const mcv = try self.resolveInst(ty_op.operand); |
| 2010 | 2203 | switch (mcv) { |
| 2011 | .dead, .unreach => unreachable, | |
| 2012 | 2204 | .register => unreachable, // a slice doesn't fit in one register |
| 2013 | 2205 | .stack_argument_offset => |off| { |
| 2014 | 2206 | break :result MCValue{ .stack_argument_offset = off + 4 }; |
| ... | ... | @@ -2019,7 +2211,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 2019 | 2211 | .memory => |addr| { |
| 2020 | 2212 | break :result MCValue{ .memory = addr + 4 }; |
| 2021 | 2213 | }, |
| 2022 | else => return self.fail("TODO implement slice_len for {}", .{mcv}), | |
| 2214 | else => unreachable, // invalid MCValue for a slice | |
| 2023 | 2215 | } |
| 2024 | 2216 | }; |
| 2025 | 2217 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | ... | @@ -2034,7 +2226,12 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2034 | 2226 | .ptr_stack_offset => |off| { |
| 2035 | 2227 | break :result MCValue{ .ptr_stack_offset = off - 4 }; |
| 2036 | 2228 | }, |
| 2037 | else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), | |
| 2229 | else => { | |
| 2230 | const lhs_bind: ReadArg.Bind = .{ .mcv = mcv }; | |
| 2231 | const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 4 } }; | |
| 2232 | ||
| 2233 | break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null); | |
| 2234 | }, | |
| 2038 | 2235 | } |
| 2039 | 2236 | }; |
| 2040 | 2237 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | ... | @@ -2049,91 +2246,96 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2049 | 2246 | .ptr_stack_offset => |off| { |
| 2050 | 2247 | break :result MCValue{ .ptr_stack_offset = off }; |
| 2051 | 2248 | }, |
| 2052 | else => return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{mcv}), | |
| 2249 | else => { | |
| 2250 | if (self.reuseOperand(inst, ty_op.operand, 0, mcv)) { | |
| 2251 | break :result mcv; | |
| 2252 | } else { | |
| 2253 | break :result MCValue{ .register = try self.copyToTmpRegister(Type.usize, mcv) }; | |
| 2254 | } | |
| 2255 | }, | |
| 2053 | 2256 | } |
| 2054 | 2257 | }; |
| 2055 | 2258 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2056 | 2259 | } |
| 2057 | 2260 | |
| 2058 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | |
| 2059 | const is_volatile = false; // TODO | |
| 2060 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2061 | ||
| 2062 | if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 2063 | const result: MCValue = result: { | |
| 2064 | const slice_mcv = try self.resolveInst(bin_op.lhs); | |
| 2065 | ||
| 2066 | // TODO optimize for the case where the index is a constant, | |
| 2067 | // i.e. index_mcv == .immediate | |
| 2068 | const index_mcv = try self.resolveInst(bin_op.rhs); | |
| 2069 | const index_is_register = index_mcv == .register; | |
| 2070 | ||
| 2071 | const slice_ty = self.air.typeOf(bin_op.lhs); | |
| 2072 | const elem_ty = slice_ty.childType(); | |
| 2073 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); | |
| 2074 | ||
| 2075 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 2076 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); | |
| 2261 | fn ptrElemVal( | |
| 2262 | self: *Self, | |
| 2263 | ptr_bind: ReadArg.Bind, | |
| 2264 | index_bind: ReadArg.Bind, | |
| 2265 | ptr_ty: Type, | |
| 2266 | maybe_inst: ?Air.Inst.Index, | |
| 2267 | ) !MCValue { | |
| 2268 | const elem_ty = ptr_ty.childType(); | |
| 2269 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); | |
| 2077 | 2270 | |
| 2078 | const index_lock: ?RegisterLock = if (index_is_register) | |
| 2079 | self.register_manager.lockRegAssumeUnused(index_mcv.register) | |
| 2080 | else | |
| 2081 | null; | |
| 2082 | defer if (index_lock) |reg| self.register_manager.unlockReg(reg); | |
| 2271 | switch (elem_size) { | |
| 2272 | 1, 4 => { | |
| 2273 | var base_reg: Register = undefined; | |
| 2274 | var index_reg: Register = undefined; | |
| 2275 | var dest_reg: Register = undefined; | |
| 2083 | 2276 | |
| 2084 | const base_mcv = slicePtr(slice_mcv); | |
| 2277 | const read_args = [_]ReadArg{ | |
| 2278 | .{ .ty = ptr_ty, .bind = ptr_bind, .class = gp, .reg = &base_reg }, | |
| 2279 | .{ .ty = Type.usize, .bind = index_bind, .class = gp, .reg = &index_reg }, | |
| 2280 | }; | |
| 2281 | const write_args = [_]WriteArg{ | |
| 2282 | .{ .ty = elem_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 2283 | }; | |
| 2284 | try self.allocRegs( | |
| 2285 | &read_args, | |
| 2286 | &write_args, | |
| 2287 | if (maybe_inst) |inst| .{ | |
| 2288 | .corresponding_inst = inst, | |
| 2289 | .operand_mapping = &.{ 0, 1 }, | |
| 2290 | } else null, | |
| 2291 | ); | |
| 2292 | ||
| 2293 | const tag: Mir.Inst.Tag = switch (elem_size) { | |
| 2294 | 1 => .ldrb, | |
| 2295 | 4 => .ldr, | |
| 2296 | else => unreachable, | |
| 2297 | }; | |
| 2298 | const shift: u5 = switch (elem_size) { | |
| 2299 | 1 => 0, | |
| 2300 | 4 => 2, | |
| 2301 | else => unreachable, | |
| 2302 | }; | |
| 2085 | 2303 | |
| 2086 | switch (elem_size) { | |
| 2087 | 1, 4 => { | |
| 2088 | const base_reg = switch (base_mcv) { | |
| 2089 | .register => |r| r, | |
| 2090 | else => try self.copyToTmpRegister(slice_ptr_field_type, base_mcv), | |
| 2091 | }; | |
| 2092 | const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg); | |
| 2093 | defer self.register_manager.unlockReg(base_reg_lock); | |
| 2304 | _ = try self.addInst(.{ | |
| 2305 | .tag = tag, | |
| 2306 | .data = .{ .rr_offset = .{ | |
| 2307 | .rt = dest_reg, | |
| 2308 | .rn = base_reg, | |
| 2309 | .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) }, | |
| 2310 | } }, | |
| 2311 | }); | |
| 2094 | 2312 | |
| 2095 | const dst_reg = try self.register_manager.allocReg(inst, gp); | |
| 2096 | const dst_mcv = MCValue{ .register = dst_reg }; | |
| 2097 | const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); | |
| 2098 | defer self.register_manager.unlockReg(dst_reg_lock); | |
| 2313 | return MCValue{ .register = dest_reg }; | |
| 2314 | }, | |
| 2315 | else => { | |
| 2316 | const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null); | |
| 2099 | 2317 | |
| 2100 | const index_reg: Register = switch (index_mcv) { | |
| 2101 | .register => |reg| reg, | |
| 2102 | else => try self.copyToTmpRegister(Type.usize, index_mcv), | |
| 2103 | }; | |
| 2104 | const index_reg_lock = self.register_manager.lockReg(index_reg); | |
| 2105 | defer if (index_reg_lock) |lock| self.register_manager.unlockReg(lock); | |
| 2318 | const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst); | |
| 2319 | try self.load(dest, addr, ptr_ty); | |
| 2320 | return dest; | |
| 2321 | }, | |
| 2322 | } | |
| 2323 | } | |
| 2106 | 2324 | |
| 2107 | const tag: Mir.Inst.Tag = switch (elem_size) { | |
| 2108 | 1 => .ldrb, | |
| 2109 | 4 => .ldr, | |
| 2110 | else => unreachable, | |
| 2111 | }; | |
| 2112 | const shift: u5 = switch (elem_size) { | |
| 2113 | 1 => 0, | |
| 2114 | 4 => 2, | |
| 2115 | else => unreachable, | |
| 2116 | }; | |
| 2325 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | |
| 2326 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2327 | const slice_ty = self.air.typeOf(bin_op.lhs); | |
| 2328 | const result: MCValue = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { | |
| 2329 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 2330 | const ptr_ty = slice_ty.slicePtrFieldType(&buf); | |
| 2117 | 2331 | |
| 2118 | _ = try self.addInst(.{ | |
| 2119 | .tag = tag, | |
| 2120 | .data = .{ .rr_offset = .{ | |
| 2121 | .rt = dst_reg, | |
| 2122 | .rn = base_reg, | |
| 2123 | .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) }, | |
| 2124 | } }, | |
| 2125 | }); | |
| 2332 | const slice_mcv = try self.resolveInst(bin_op.lhs); | |
| 2333 | const base_mcv = slicePtr(slice_mcv); | |
| 2126 | 2334 | |
| 2127 | break :result dst_mcv; | |
| 2128 | }, | |
| 2129 | else => { | |
| 2130 | const dest = try self.allocRegOrMem(inst, true); | |
| 2131 | const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); | |
| 2132 | try self.load(dest, addr, slice_ptr_field_type); | |
| 2335 | const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; | |
| 2336 | const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; | |
| 2133 | 2337 | |
| 2134 | break :result dest; | |
| 2135 | }, | |
| 2136 | } | |
| 2338 | break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); | |
| 2137 | 2339 | }; |
| 2138 | 2340 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2139 | 2341 | } |
| ... | ... | @@ -2143,40 +2345,108 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2143 | 2345 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2144 | 2346 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2145 | 2347 | const slice_mcv = try self.resolveInst(extra.lhs); |
| 2146 | const index_mcv = try self.resolveInst(extra.rhs); | |
| 2147 | 2348 | const base_mcv = slicePtr(slice_mcv); |
| 2148 | 2349 | |
| 2350 | const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; | |
| 2351 | const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; | |
| 2352 | ||
| 2149 | 2353 | const slice_ty = self.air.typeOf(extra.lhs); |
| 2354 | const index_ty = self.air.typeOf(extra.rhs); | |
| 2150 | 2355 | |
| 2151 | const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ty, Type.usize, null); | |
| 2356 | const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null); | |
| 2152 | 2357 | break :result addr; |
| 2153 | 2358 | }; |
| 2154 | 2359 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 2155 | 2360 | } |
| 2156 | 2361 | |
| 2157 | fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { | |
| 2158 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2159 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch}); | |
| 2160 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 2161 | } | |
| 2162 | ||
| 2163 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | |
| 2164 | const is_volatile = false; // TODO | |
| 2165 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2166 | const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch}); | |
| 2167 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 2168 | } | |
| 2362 | fn arrayElemVal( | |
| 2363 | self: *Self, | |
| 2364 | array_bind: ReadArg.Bind, | |
| 2365 | index_bind: ReadArg.Bind, | |
| 2366 | array_ty: Type, | |
| 2367 | maybe_inst: ?Air.Inst.Index, | |
| 2368 | ) InnerError!MCValue { | |
| 2369 | const elem_ty = array_ty.childType(); | |
| 2169 | 2370 | |
| 2170 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { | |
| 2171 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 2172 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 2173 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | |
| 2174 | const ptr_mcv = try self.resolveInst(extra.lhs); | |
| 2175 | const index_mcv = try self.resolveInst(extra.rhs); | |
| 2371 | const mcv = try array_bind.resolveToMcv(self); | |
| 2372 | switch (mcv) { | |
| 2373 | .stack_offset, | |
| 2374 | .memory, | |
| 2375 | .stack_argument_offset, | |
| 2376 | => { | |
| 2377 | const ptr_to_mcv = switch (mcv) { | |
| 2378 | .stack_offset => |off| MCValue{ .ptr_stack_offset = off }, | |
| 2379 | .memory => |addr| MCValue{ .immediate = @intCast(u32, addr) }, | |
| 2380 | .stack_argument_offset => |off| blk: { | |
| 2381 | const reg = try self.register_manager.allocReg(null, gp); | |
| 2176 | 2382 | |
| 2177 | const ptr_ty = self.air.typeOf(extra.lhs); | |
| 2383 | _ = try self.addInst(.{ | |
| 2384 | .tag = .ldr_ptr_stack_argument, | |
| 2385 | .data = .{ .r_stack_offset = .{ | |
| 2386 | .rt = reg, | |
| 2387 | .stack_offset = off, | |
| 2388 | } }, | |
| 2389 | }); | |
| 2390 | ||
| 2391 | break :blk MCValue{ .register = reg }; | |
| 2392 | }, | |
| 2393 | else => unreachable, | |
| 2394 | }; | |
| 2395 | const ptr_to_mcv_lock: ?RegisterLock = switch (ptr_to_mcv) { | |
| 2396 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | |
| 2397 | else => null, | |
| 2398 | }; | |
| 2399 | defer if (ptr_to_mcv_lock) |lock| self.register_manager.unlockReg(lock); | |
| 2400 | ||
| 2401 | const base_bind: ReadArg.Bind = .{ .mcv = ptr_to_mcv }; | |
| 2402 | ||
| 2403 | var ptr_ty_payload: Type.Payload.ElemType = .{ | |
| 2404 | .base = .{ .tag = .single_mut_pointer }, | |
| 2405 | .data = elem_ty, | |
| 2406 | }; | |
| 2407 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); | |
| 2408 | ||
| 2409 | return try self.ptrElemVal(base_bind, index_bind, ptr_ty, maybe_inst); | |
| 2410 | }, | |
| 2411 | else => return self.fail("TODO implement array_elem_val for {}", .{mcv}), | |
| 2412 | } | |
| 2413 | } | |
| 2414 | ||
| 2415 | fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { | |
| 2416 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2417 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | |
| 2418 | const array_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; | |
| 2419 | const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; | |
| 2420 | const array_ty = self.air.typeOf(bin_op.lhs); | |
| 2178 | 2421 | |
| 2179 | const addr = try self.binOp(.ptr_add, ptr_mcv, index_mcv, ptr_ty, Type.usize, null); | |
| 2422 | break :result try self.arrayElemVal(array_bind, index_bind, array_ty, inst); | |
| 2423 | }; | |
| 2424 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 2425 | } | |
| 2426 | ||
| 2427 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | |
| 2428 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2429 | const ptr_ty = self.air.typeOf(bin_op.lhs); | |
| 2430 | const result: MCValue = if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { | |
| 2431 | const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; | |
| 2432 | const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; | |
| 2433 | ||
| 2434 | break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); | |
| 2435 | }; | |
| 2436 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 2437 | } | |
| 2438 | ||
| 2439 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { | |
| 2440 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 2441 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 2442 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | |
| 2443 | const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs }; | |
| 2444 | const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; | |
| 2445 | ||
| 2446 | const ptr_ty = self.air.typeOf(extra.lhs); | |
| 2447 | const index_ty = self.air.typeOf(extra.rhs); | |
| 2448 | ||
| 2449 | const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null); | |
| 2180 | 2450 | break :result addr; |
| 2181 | 2451 | }; |
| 2182 | 2452 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| ... | ... | @@ -2240,7 +2510,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 2240 | 2510 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2241 | 2511 | } |
| 2242 | 2512 | |
| 2243 | fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { | |
| 2513 | fn reuseOperand( | |
| 2514 | self: *Self, | |
| 2515 | inst: Air.Inst.Index, | |
| 2516 | operand: Air.Inst.Ref, | |
| 2517 | op_index: Liveness.OperandInt, | |
| 2518 | mcv: MCValue, | |
| 2519 | ) bool { | |
| 2244 | 2520 | if (!self.liveness.operandDies(inst, op_index)) |
| 2245 | 2521 | return false; |
| 2246 | 2522 | |
| ... | ... | @@ -2362,16 +2638,18 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 2362 | 2638 | if (self.liveness.isUnused(inst) and !is_volatile) |
| 2363 | 2639 | break :result MCValue.dead; |
| 2364 | 2640 | |
| 2365 | const dst_mcv: MCValue = blk: { | |
| 2366 | if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { | |
| 2641 | const dest_mcv: MCValue = blk: { | |
| 2642 | const ptr_fits_dest = elem_ty.abiSize(self.target.*) <= 4; | |
| 2643 | if (ptr_fits_dest and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { | |
| 2367 | 2644 | // The MCValue that holds the pointer can be re-used as the value. |
| 2368 | 2645 | break :blk ptr; |
| 2369 | 2646 | } else { |
| 2370 | break :blk try self.allocRegOrMem(inst, true); | |
| 2647 | break :blk try self.allocRegOrMem(elem_ty, true, inst); | |
| 2371 | 2648 | } |
| 2372 | 2649 | }; |
| 2373 | try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); | |
| 2374 | break :result dst_mcv; | |
| 2650 | try self.load(dest_mcv, ptr, self.air.typeOf(ty_op.operand)); | |
| 2651 | ||
| 2652 | break :result dest_mcv; | |
| 2375 | 2653 | }; |
| 2376 | 2654 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2377 | 2655 | } |
| ... | ... | @@ -2498,26 +2776,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 2498 | 2776 | break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; |
| 2499 | 2777 | }, |
| 2500 | 2778 | else => { |
| 2501 | const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ | |
| 2502 | .immediate = struct_field_offset, | |
| 2503 | }); | |
| 2504 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); | |
| 2505 | defer self.register_manager.unlockReg(offset_reg_lock); | |
| 2506 | ||
| 2507 | const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv); | |
| 2508 | const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); | |
| 2509 | defer self.register_manager.unlockReg(addr_reg_lock); | |
| 2510 | ||
| 2511 | const dest = try self.binOp( | |
| 2512 | .add, | |
| 2513 | .{ .register = addr_reg }, | |
| 2514 | .{ .register = offset_reg }, | |
| 2515 | Type.usize, | |
| 2516 | Type.usize, | |
| 2517 | null, | |
| 2518 | ); | |
| 2779 | const lhs_bind: ReadArg.Bind = .{ .mcv = mcv }; | |
| 2780 | const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } }; | |
| 2519 | 2781 | |
| 2520 | break :result dest; | |
| 2782 | break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null); | |
| 2521 | 2783 | }, |
| 2522 | 2784 | } |
| 2523 | 2785 | }; |
| ... | ... | @@ -2532,6 +2794,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2532 | 2794 | const mcv = try self.resolveInst(operand); |
| 2533 | 2795 | const struct_ty = self.air.typeOf(operand); |
| 2534 | 2796 | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); |
| 2797 | const struct_field_ty = struct_ty.structFieldType(index); | |
| 2535 | 2798 | |
| 2536 | 2799 | switch (mcv) { |
| 2537 | 2800 | .dead, .unreach => unreachable, |
| ... | ... | @@ -2569,11 +2832,45 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2569 | 2832 | } else { |
| 2570 | 2833 | // Copy to new register |
| 2571 | 2834 | const dest_reg = try self.register_manager.allocReg(null, gp); |
| 2572 | try self.genSetReg(struct_ty.structFieldType(index), dest_reg, field); | |
| 2835 | try self.genSetReg(struct_field_ty, dest_reg, field); | |
| 2573 | 2836 | |
| 2574 | 2837 | break :result MCValue{ .register = dest_reg }; |
| 2575 | 2838 | } |
| 2576 | 2839 | }, |
| 2840 | .register => { | |
| 2841 | var operand_reg: Register = undefined; | |
| 2842 | var dest_reg: Register = undefined; | |
| 2843 | ||
| 2844 | const read_args = [_]ReadArg{ | |
| 2845 | .{ .ty = struct_ty, .bind = .{ .mcv = mcv }, .class = gp, .reg = &operand_reg }, | |
| 2846 | }; | |
| 2847 | const write_args = [_]WriteArg{ | |
| 2848 | .{ .ty = struct_field_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 2849 | }; | |
| 2850 | try self.allocRegs( | |
| 2851 | &read_args, | |
| 2852 | &write_args, | |
| 2853 | ReuseMetadata{ | |
| 2854 | .corresponding_inst = inst, | |
| 2855 | .operand_mapping = &.{0}, | |
| 2856 | }, | |
| 2857 | ); | |
| 2858 | ||
| 2859 | const field_bit_offset = struct_field_offset * 8; | |
| 2860 | const field_bit_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)) * 8; | |
| 2861 | ||
| 2862 | _ = try self.addInst(.{ | |
| 2863 | .tag = if (struct_field_ty.isSignedInt()) Mir.Inst.Tag.sbfx else .ubfx, | |
| 2864 | .data = .{ .rr_lsb_width = .{ | |
| 2865 | .rd = dest_reg, | |
| 2866 | .rn = operand_reg, | |
| 2867 | .lsb = @intCast(u5, field_bit_offset), | |
| 2868 | .width = @intCast(u6, field_bit_size), | |
| 2869 | } }, | |
| 2870 | }); | |
| 2871 | ||
| 2872 | break :result MCValue{ .register = dest_reg }; | |
| 2873 | }, | |
| 2577 | 2874 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), |
| 2578 | 2875 | } |
| 2579 | 2876 | }; |
| ... | ... | @@ -2583,114 +2880,285 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2583 | 2880 | |
| 2584 | 2881 | fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2585 | 2882 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2586 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 2587 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFieldParentPtr", .{}); | |
| 2588 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 2883 | const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; | |
| 2884 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | |
| 2885 | const field_ptr = try self.resolveInst(extra.field_ptr); | |
| 2886 | const struct_ty = self.air.getRefType(ty_pl.ty).childType(); | |
| 2887 | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*)); | |
| 2888 | switch (field_ptr) { | |
| 2889 | .ptr_stack_offset => |off| { | |
| 2890 | break :result MCValue{ .ptr_stack_offset = off + struct_field_offset }; | |
| 2891 | }, | |
| 2892 | else => { | |
| 2893 | const lhs_bind: ReadArg.Bind = .{ .mcv = field_ptr }; | |
| 2894 | const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } }; | |
| 2895 | ||
| 2896 | break :result try self.addSub(.sub, lhs_bind, rhs_bind, Type.usize, Type.usize, null); | |
| 2897 | }, | |
| 2898 | } | |
| 2899 | }; | |
| 2900 | return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none }); | |
| 2589 | 2901 | } |
| 2590 | 2902 | |
| 2591 | /// Allocates a new register. If Inst in non-null, additionally tracks | |
| 2592 | /// this register and the corresponding int and removes all previous | |
| 2593 | /// tracking. Does not do the actual moving (that is handled by | |
| 2594 | /// genSetReg). | |
| 2595 | fn prepareNewRegForMoving( | |
| 2596 | self: *Self, | |
| 2597 | track_inst: ?Air.Inst.Index, | |
| 2598 | register_class: RegisterManager.RegisterBitSet, | |
| 2599 | mcv: MCValue, | |
| 2600 | ) !Register { | |
| 2601 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | |
| 2602 | const reg = try self.register_manager.allocReg(track_inst, register_class); | |
| 2903 | /// An argument to a Mir instruction which is read (and possibly also | |
| 2904 | /// written to) by the respective instruction | |
| 2905 | const ReadArg = struct { | |
| 2906 | ty: Type, | |
| 2907 | bind: Bind, | |
| 2908 | class: RegisterManager.RegisterBitSet, | |
| 2909 | reg: *Register, | |
| 2603 | 2910 | |
| 2604 | if (track_inst) |inst| { | |
| 2605 | // Overwrite the MCValue associated with this inst | |
| 2606 | branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); | |
| 2911 | const Bind = union(enum) { | |
| 2912 | inst: Air.Inst.Ref, | |
| 2913 | mcv: MCValue, | |
| 2607 | 2914 | |
| 2608 | // If the previous MCValue occupied some space we track, we | |
| 2609 | // need to make sure it is marked as free now. | |
| 2610 | switch (mcv) { | |
| 2611 | .cpsr_flags => { | |
| 2612 | assert(self.cpsr_flags_inst.? == inst); | |
| 2613 | self.cpsr_flags_inst = null; | |
| 2614 | }, | |
| 2615 | .register => |prev_reg| { | |
| 2616 | assert(!self.register_manager.isRegFree(prev_reg)); | |
| 2617 | self.register_manager.freeReg(prev_reg); | |
| 2618 | }, | |
| 2619 | else => {}, | |
| 2915 | fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue { | |
| 2916 | return switch (bind) { | |
| 2917 | .inst => |inst| try function.resolveInst(inst), | |
| 2918 | .mcv => |mcv| mcv, | |
| 2919 | }; | |
| 2620 | 2920 | } |
| 2621 | } | |
| 2622 | 2921 | |
| 2623 | return reg; | |
| 2624 | } | |
| 2922 | fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u32 { | |
| 2923 | switch (bind) { | |
| 2924 | .inst => |inst| { | |
| 2925 | // TODO resolve independently of inst_table | |
| 2926 | const mcv = try function.resolveInst(inst); | |
| 2927 | switch (mcv) { | |
| 2928 | .immediate => |imm| return imm, | |
| 2929 | else => return null, | |
| 2930 | } | |
| 2931 | }, | |
| 2932 | .mcv => |mcv| { | |
| 2933 | switch (mcv) { | |
| 2934 | .immediate => |imm| return imm, | |
| 2935 | else => return null, | |
| 2936 | } | |
| 2937 | }, | |
| 2938 | } | |
| 2939 | } | |
| 2940 | }; | |
| 2941 | }; | |
| 2942 | ||
| 2943 | /// An argument to a Mir instruction which is written to (but not read | |
| 2944 | /// from) by the respective instruction | |
| 2945 | const WriteArg = struct { | |
| 2946 | ty: Type, | |
| 2947 | bind: Bind, | |
| 2948 | class: RegisterManager.RegisterBitSet, | |
| 2949 | reg: *Register, | |
| 2950 | ||
| 2951 | const Bind = union(enum) { | |
| 2952 | reg: Register, | |
| 2953 | none: void, | |
| 2954 | }; | |
| 2955 | }; | |
| 2956 | ||
| 2957 | /// Holds all data necessary for enabling the potential reuse of | |
| 2958 | /// operand registers as destinations | |
| 2959 | const ReuseMetadata = struct { | |
| 2960 | corresponding_inst: Air.Inst.Index, | |
| 2961 | ||
| 2962 | /// Maps every element index of read_args to the corresponding | |
| 2963 | /// index in the Air instruction | |
| 2964 | /// | |
| 2965 | /// When the order of read_args corresponds exactly to the order | |
| 2966 | /// of the inputs of the Air instruction, this would be e.g. | |
| 2967 | /// &.{ 0, 1 }. However, when the order is not the same or some | |
| 2968 | /// inputs to the Air instruction are omitted (e.g. when they can | |
| 2969 | /// be represented as immediates to the Mir instruction), | |
| 2970 | /// operand_mapping should reflect that fact. | |
| 2971 | operand_mapping: []const Liveness.OperandInt, | |
| 2972 | }; | |
| 2625 | 2973 | |
| 2626 | /// Don't call this function directly. Use binOp instead. | |
| 2974 | /// Allocate a set of registers for use as arguments for a Mir | |
| 2975 | /// instruction | |
| 2627 | 2976 | /// |
| 2628 | /// Calling this function signals an intention to generate a Mir | |
| 2629 | /// instruction of the form | |
| 2977 | /// If the Mir instruction these registers are allocated for | |
| 2978 | /// corresponds exactly to a single Air instruction, populate | |
| 2979 | /// reuse_metadata in order to enable potential reuse of an operand as | |
| 2980 | /// the destination (provided that that operand dies in this | |
| 2981 | /// instruction). | |
| 2630 | 2982 | /// |
| 2631 | /// op dest, lhs, rhs | |
| 2983 | /// Reusing an operand register as destination is the only time two | |
| 2984 | /// arguments may share the same register. In all other cases, | |
| 2985 | /// allocRegs guarantees that a register will never be allocated to | |
| 2986 | /// more than one argument. | |
| 2632 | 2987 | /// |
| 2633 | /// Asserts that generating an instruction of that form is possible. | |
| 2634 | fn binOpRegister( | |
| 2988 | /// Furthermore, allocReg guarantees that all arguments which are | |
| 2989 | /// already bound to registers before calling allocRegs will not | |
| 2990 | /// change their register binding. This is done by locking these | |
| 2991 | /// registers. | |
| 2992 | fn allocRegs( | |
| 2635 | 2993 | self: *Self, |
| 2636 | mir_tag: Mir.Inst.Tag, | |
| 2637 | lhs: MCValue, | |
| 2638 | rhs: MCValue, | |
| 2639 | lhs_ty: Type, | |
| 2640 | rhs_ty: Type, | |
| 2641 | metadata: ?BinOpMetadata, | |
| 2642 | ) !MCValue { | |
| 2643 | const lhs_is_register = lhs == .register; | |
| 2644 | const rhs_is_register = rhs == .register; | |
| 2994 | read_args: []const ReadArg, | |
| 2995 | write_args: []const WriteArg, | |
| 2996 | reuse_metadata: ?ReuseMetadata, | |
| 2997 | ) InnerError!void { | |
| 2998 | // Air instructions have exactly one output | |
| 2999 | assert(!(reuse_metadata != null and write_args.len != 1)); // see note above | |
| 3000 | ||
| 3001 | // The operand mapping is a 1:1 mapping of read args to their | |
| 3002 | // corresponding operand index in the Air instruction | |
| 3003 | assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above | |
| 3004 | ||
| 3005 | const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len); | |
| 3006 | defer self.gpa.free(locks); | |
| 3007 | const read_locks = locks[0..read_args.len]; | |
| 3008 | const write_locks = locks[read_args.len..]; | |
| 3009 | ||
| 3010 | std.mem.set(?RegisterLock, locks, null); | |
| 3011 | defer for (locks) |lock| { | |
| 3012 | if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg); | |
| 3013 | }; | |
| 2645 | 3014 | |
| 2646 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | |
| 2647 | self.register_manager.lockReg(lhs.register) | |
| 2648 | else | |
| 2649 | null; | |
| 2650 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 3015 | // When we reuse a read_arg as a destination, the corresponding | |
| 3016 | // MCValue of the read_arg will be set to .dead. In that case, we | |
| 3017 | // skip allocating this read_arg. | |
| 3018 | var reused_read_arg: ?usize = null; | |
| 2651 | 3019 | |
| 2652 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | |
| 2653 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | |
| 2654 | break :inst Air.refToIndex(md.lhs).?; | |
| 2655 | } else null; | |
| 3020 | // Lock all args which are already allocated to registers | |
| 3021 | for (read_args) |arg, i| { | |
| 3022 | const mcv = try arg.bind.resolveToMcv(self); | |
| 3023 | if (mcv == .register) { | |
| 3024 | read_locks[i] = self.register_manager.lockReg(mcv.register); | |
| 3025 | } | |
| 3026 | } | |
| 2656 | 3027 | |
| 2657 | break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); | |
| 2658 | }; | |
| 2659 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | |
| 2660 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 3028 | for (write_args) |arg, i| { | |
| 3029 | if (arg.bind == .reg) { | |
| 3030 | write_locks[i] = self.register_manager.lockReg(arg.bind.reg); | |
| 3031 | } | |
| 3032 | } | |
| 2661 | 3033 | |
| 2662 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { | |
| 2663 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | |
| 2664 | break :inst Air.refToIndex(md.rhs).?; | |
| 2665 | } else null; | |
| 3034 | // Allocate registers for all args which aren't allocated to | |
| 3035 | // registers yet | |
| 3036 | for (read_args) |arg, i| { | |
| 3037 | const mcv = try arg.bind.resolveToMcv(self); | |
| 3038 | if (mcv == .register) { | |
| 3039 | arg.reg.* = mcv.register; | |
| 3040 | } else { | |
| 3041 | const track_inst: ?Air.Inst.Index = switch (arg.bind) { | |
| 3042 | .inst => |inst| Air.refToIndex(inst).?, | |
| 3043 | else => null, | |
| 3044 | }; | |
| 3045 | arg.reg.* = try self.register_manager.allocReg(track_inst, arg.class); | |
| 3046 | read_locks[i] = self.register_manager.lockReg(arg.reg.*); | |
| 3047 | } | |
| 3048 | } | |
| 2666 | 3049 | |
| 2667 | break :blk try self.prepareNewRegForMoving(track_inst, gp, rhs); | |
| 2668 | }; | |
| 2669 | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); | |
| 2670 | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 2671 | ||
| 2672 | const dest_reg = switch (mir_tag) { | |
| 2673 | .cmp => .r0, // cmp has no destination regardless | |
| 2674 | else => if (metadata) |md| blk: { | |
| 2675 | if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { | |
| 2676 | break :blk lhs_reg; | |
| 2677 | } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { | |
| 2678 | break :blk rhs_reg; | |
| 3050 | if (reuse_metadata != null) { | |
| 3051 | const inst = reuse_metadata.?.corresponding_inst; | |
| 3052 | const operand_mapping = reuse_metadata.?.operand_mapping; | |
| 3053 | const arg = write_args[0]; | |
| 3054 | if (arg.bind == .reg) { | |
| 3055 | arg.reg.* = arg.bind.reg; | |
| 3056 | } else { | |
| 3057 | reuse_operand: for (read_args) |read_arg, i| { | |
| 3058 | if (read_arg.bind == .inst) { | |
| 3059 | const operand = read_arg.bind.inst; | |
| 3060 | const mcv = try self.resolveInst(operand); | |
| 3061 | if (mcv == .register and | |
| 3062 | std.meta.eql(arg.class, read_arg.class) and | |
| 3063 | self.reuseOperand(inst, operand, operand_mapping[i], mcv)) | |
| 3064 | { | |
| 3065 | arg.reg.* = mcv.register; | |
| 3066 | write_locks[0] = null; | |
| 3067 | reused_read_arg = i; | |
| 3068 | break :reuse_operand; | |
| 3069 | } | |
| 3070 | } | |
| 2679 | 3071 | } else { |
| 2680 | break :blk try self.register_manager.allocReg(md.inst, gp); | |
| 3072 | arg.reg.* = try self.register_manager.allocReg(inst, arg.class); | |
| 3073 | write_locks[0] = self.register_manager.lockReg(arg.reg.*); | |
| 2681 | 3074 | } |
| 2682 | } else try self.register_manager.allocReg(null, gp), | |
| 2683 | }; | |
| 3075 | } | |
| 3076 | } else { | |
| 3077 | for (write_args) |arg, i| { | |
| 3078 | if (arg.bind == .reg) { | |
| 3079 | arg.reg.* = arg.bind.reg; | |
| 3080 | } else { | |
| 3081 | arg.reg.* = try self.register_manager.allocReg(null, arg.class); | |
| 3082 | write_locks[i] = self.register_manager.lockReg(arg.reg.*); | |
| 3083 | } | |
| 3084 | } | |
| 3085 | } | |
| 3086 | ||
| 3087 | // For all read_args which need to be moved from non-register to | |
| 3088 | // register, perform the move | |
| 3089 | for (read_args) |arg, i| { | |
| 3090 | if (reused_read_arg) |j| { | |
| 3091 | // Check whether this read_arg was reused | |
| 3092 | if (i == j) continue; | |
| 3093 | } | |
| 3094 | ||
| 3095 | const mcv = try arg.bind.resolveToMcv(self); | |
| 3096 | if (mcv != .register) { | |
| 3097 | if (arg.bind == .inst) { | |
| 3098 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | |
| 3099 | const inst = Air.refToIndex(arg.bind.inst).?; | |
| 3100 | ||
| 3101 | // Overwrite the MCValue associated with this inst | |
| 3102 | branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* }); | |
| 3103 | ||
| 3104 | // If the previous MCValue occupied some space we track, we | |
| 3105 | // need to make sure it is marked as free now. | |
| 3106 | switch (mcv) { | |
| 3107 | .cpsr_flags => { | |
| 3108 | assert(self.cpsr_flags_inst.? == inst); | |
| 3109 | self.cpsr_flags_inst = null; | |
| 3110 | }, | |
| 3111 | .register => |prev_reg| { | |
| 3112 | assert(!self.register_manager.isRegFree(prev_reg)); | |
| 3113 | self.register_manager.freeReg(prev_reg); | |
| 3114 | }, | |
| 3115 | else => {}, | |
| 3116 | } | |
| 3117 | } | |
| 3118 | ||
| 3119 | try self.genSetReg(arg.ty, arg.reg.*, mcv); | |
| 3120 | } | |
| 3121 | } | |
| 3122 | } | |
| 2684 | 3123 | |
| 2685 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | |
| 2686 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | |
| 3124 | /// Wrapper around allocRegs and addInst tailored for specific Mir | |
| 3125 | /// instructions which are binary operations acting on two registers | |
| 3126 | /// | |
| 3127 | /// Returns the destination register | |
| 3128 | fn binOpRegisterNew( | |
| 3129 | self: *Self, | |
| 3130 | mir_tag: Mir.Inst.Tag, | |
| 3131 | lhs_bind: ReadArg.Bind, | |
| 3132 | rhs_bind: ReadArg.Bind, | |
| 3133 | lhs_ty: Type, | |
| 3134 | rhs_ty: Type, | |
| 3135 | maybe_inst: ?Air.Inst.Index, | |
| 3136 | ) !MCValue { | |
| 3137 | var lhs_reg: Register = undefined; | |
| 3138 | var rhs_reg: Register = undefined; | |
| 3139 | var dest_reg: Register = undefined; | |
| 3140 | ||
| 3141 | const read_args = [_]ReadArg{ | |
| 3142 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, | |
| 3143 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, | |
| 3144 | }; | |
| 3145 | const write_args = [_]WriteArg{ | |
| 3146 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 3147 | }; | |
| 3148 | try self.allocRegs( | |
| 3149 | &read_args, | |
| 3150 | &write_args, | |
| 3151 | if (maybe_inst) |inst| .{ | |
| 3152 | .corresponding_inst = inst, | |
| 3153 | .operand_mapping = &.{ 0, 1 }, | |
| 3154 | } else null, | |
| 3155 | ); | |
| 2687 | 3156 | |
| 2688 | 3157 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2689 | 3158 | .add, |
| 2690 | 3159 | .adds, |
| 2691 | 3160 | .sub, |
| 2692 | 3161 | .subs, |
| 2693 | .cmp, | |
| 2694 | 3162 | .@"and", |
| 2695 | 3163 | .orr, |
| 2696 | 3164 | .eor, |
| ... | ... | @@ -2725,78 +3193,51 @@ fn binOpRegister( |
| 2725 | 3193 | return MCValue{ .register = dest_reg }; |
| 2726 | 3194 | } |
| 2727 | 3195 | |
| 2728 | /// Don't call this function directly. Use binOp instead. | |
| 2729 | /// | |
| 2730 | /// Calling this function signals an intention to generate a Mir | |
| 2731 | /// instruction of the form | |
| 2732 | /// | |
| 2733 | /// op dest, lhs, #rhs_imm | |
| 3196 | /// Wrapper around allocRegs and addInst tailored for specific Mir | |
| 3197 | /// instructions which are binary operations acting on a register and | |
| 3198 | /// an immediate | |
| 2734 | 3199 | /// |
| 2735 | /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to | |
| 2736 | /// rhs and vice versa. This parameter is only used when maybe_inst != | |
| 2737 | /// null. | |
| 2738 | /// | |
| 2739 | /// Asserts that generating an instruction of that form is possible. | |
| 2740 | fn binOpImmediate( | |
| 3200 | /// Returns the destination register | |
| 3201 | fn binOpImmediateNew( | |
| 2741 | 3202 | self: *Self, |
| 2742 | 3203 | mir_tag: Mir.Inst.Tag, |
| 2743 | lhs: MCValue, | |
| 2744 | rhs: MCValue, | |
| 3204 | lhs_bind: ReadArg.Bind, | |
| 3205 | rhs_immediate: u32, | |
| 2745 | 3206 | lhs_ty: Type, |
| 2746 | 3207 | lhs_and_rhs_swapped: bool, |
| 2747 | metadata: ?BinOpMetadata, | |
| 3208 | maybe_inst: ?Air.Inst.Index, | |
| 2748 | 3209 | ) !MCValue { |
| 2749 | const lhs_is_register = lhs == .register; | |
| 2750 | ||
| 2751 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | |
| 2752 | self.register_manager.lockReg(lhs.register) | |
| 2753 | else | |
| 2754 | null; | |
| 2755 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 2756 | ||
| 2757 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | |
| 2758 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | |
| 2759 | break :inst Air.refToIndex( | |
| 2760 | if (lhs_and_rhs_swapped) md.rhs else md.lhs, | |
| 2761 | ).?; | |
| 2762 | } else null; | |
| 3210 | var lhs_reg: Register = undefined; | |
| 3211 | var dest_reg: Register = undefined; | |
| 2763 | 3212 | |
| 2764 | break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); | |
| 3213 | const read_args = [_]ReadArg{ | |
| 3214 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, | |
| 2765 | 3215 | }; |
| 2766 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | |
| 2767 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 2768 | ||
| 2769 | const dest_reg = switch (mir_tag) { | |
| 2770 | .cmp => .r0, // cmp has no destination reg | |
| 2771 | else => if (metadata) |md| blk: { | |
| 2772 | if (lhs_is_register and self.reuseOperand( | |
| 2773 | md.inst, | |
| 2774 | if (lhs_and_rhs_swapped) md.rhs else md.lhs, | |
| 2775 | if (lhs_and_rhs_swapped) 1 else 0, | |
| 2776 | lhs, | |
| 2777 | )) { | |
| 2778 | break :blk lhs_reg; | |
| 2779 | } else { | |
| 2780 | break :blk try self.register_manager.allocReg(md.inst, gp); | |
| 2781 | } | |
| 2782 | } else try self.register_manager.allocReg(null, gp), | |
| 3216 | const write_args = [_]WriteArg{ | |
| 3217 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 2783 | 3218 | }; |
| 2784 | ||
| 2785 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | |
| 3219 | const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0}; | |
| 3220 | try self.allocRegs( | |
| 3221 | &read_args, | |
| 3222 | &write_args, | |
| 3223 | if (maybe_inst) |inst| .{ | |
| 3224 | .corresponding_inst = inst, | |
| 3225 | .operand_mapping = operand_mapping, | |
| 3226 | } else null, | |
| 3227 | ); | |
| 2786 | 3228 | |
| 2787 | 3229 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2788 | 3230 | .add, |
| 2789 | 3231 | .adds, |
| 2790 | 3232 | .sub, |
| 2791 | 3233 | .subs, |
| 2792 | .cmp, | |
| 2793 | 3234 | .@"and", |
| 2794 | 3235 | .orr, |
| 2795 | 3236 | .eor, |
| 2796 | 3237 | => .{ .rr_op = .{ |
| 2797 | 3238 | .rd = dest_reg, |
| 2798 | 3239 | .rn = lhs_reg, |
| 2799 | .op = Instruction.Operand.fromU32(rhs.immediate).?, | |
| 3240 | .op = Instruction.Operand.fromU32(rhs_immediate).?, | |
| 2800 | 3241 | } }, |
| 2801 | 3242 | .lsl, |
| 2802 | 3243 | .asr, |
| ... | ... | @@ -2804,7 +3245,7 @@ fn binOpImmediate( |
| 2804 | 3245 | => .{ .rr_shift = .{ |
| 2805 | 3246 | .rd = dest_reg, |
| 2806 | 3247 | .rm = lhs_reg, |
| 2807 | .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs.immediate)), | |
| 3248 | .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs_immediate)), | |
| 2808 | 3249 | } }, |
| 2809 | 3250 | else => unreachable, |
| 2810 | 3251 | }; |
| ... | ... | @@ -2817,417 +3258,502 @@ fn binOpImmediate( |
| 2817 | 3258 | return MCValue{ .register = dest_reg }; |
| 2818 | 3259 | } |
| 2819 | 3260 | |
| 2820 | const BinOpMetadata = struct { | |
| 2821 | inst: Air.Inst.Index, | |
| 2822 | lhs: Air.Inst.Ref, | |
| 2823 | rhs: Air.Inst.Ref, | |
| 2824 | }; | |
| 2825 | ||
| 2826 | /// For all your binary operation needs, this function will generate | |
| 2827 | /// the corresponding Mir instruction(s). Returns the location of the | |
| 2828 | /// result. | |
| 2829 | /// | |
| 2830 | /// If the binary operation itself happens to be an Air instruction, | |
| 2831 | /// pass the corresponding index in the inst parameter. That helps | |
| 2832 | /// this function do stuff like reusing operands. | |
| 2833 | /// | |
| 2834 | /// This function does not do any lowering to Mir itself, but instead | |
| 2835 | /// looks at the lhs and rhs and determines which kind of lowering | |
| 2836 | /// would be best suitable and then delegates the lowering to other | |
| 2837 | /// functions. | |
| 2838 | fn binOp( | |
| 3261 | fn addSub( | |
| 2839 | 3262 | self: *Self, |
| 2840 | 3263 | tag: Air.Inst.Tag, |
| 2841 | lhs: MCValue, | |
| 2842 | rhs: MCValue, | |
| 3264 | lhs_bind: ReadArg.Bind, | |
| 3265 | rhs_bind: ReadArg.Bind, | |
| 2843 | 3266 | lhs_ty: Type, |
| 2844 | 3267 | rhs_ty: Type, |
| 2845 | metadata: ?BinOpMetadata, | |
| 3268 | maybe_inst: ?Air.Inst.Index, | |
| 2846 | 3269 | ) InnerError!MCValue { |
| 2847 | switch (tag) { | |
| 2848 | .add, | |
| 2849 | .sub, | |
| 2850 | .cmp_eq, | |
| 2851 | => { | |
| 2852 | switch (lhs_ty.zigTypeTag()) { | |
| 2853 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 2854 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 2855 | .Int => { | |
| 2856 | const mod = self.bin_file.options.module.?; | |
| 2857 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 2858 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 2859 | if (int_info.bits <= 32) { | |
| 2860 | // Only say yes if the operation is | |
| 2861 | // commutative, i.e. we can swap both of the | |
| 2862 | // operands | |
| 2863 | const lhs_immediate_ok = switch (tag) { | |
| 2864 | .add => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null, | |
| 2865 | .sub, | |
| 2866 | .cmp_eq, | |
| 2867 | => false, | |
| 2868 | else => unreachable, | |
| 2869 | }; | |
| 2870 | const rhs_immediate_ok = switch (tag) { | |
| 2871 | .add, | |
| 2872 | .sub, | |
| 2873 | .cmp_eq, | |
| 2874 | => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null, | |
| 2875 | else => unreachable, | |
| 2876 | }; | |
| 3270 | switch (lhs_ty.zigTypeTag()) { | |
| 3271 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 3272 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3273 | .Int => { | |
| 3274 | const mod = self.bin_file.options.module.?; | |
| 3275 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 3276 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3277 | if (int_info.bits <= 32) { | |
| 3278 | const lhs_immediate = try lhs_bind.resolveToImmediate(self); | |
| 3279 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); | |
| 3280 | ||
| 3281 | // Only say yes if the operation is | |
| 3282 | // commutative, i.e. we can swap both of the | |
| 3283 | // operands | |
| 3284 | const lhs_immediate_ok = switch (tag) { | |
| 3285 | .add => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false, | |
| 3286 | .sub => false, | |
| 3287 | else => unreachable, | |
| 3288 | }; | |
| 3289 | const rhs_immediate_ok = switch (tag) { | |
| 3290 | .add, | |
| 3291 | .sub, | |
| 3292 | => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false, | |
| 3293 | else => unreachable, | |
| 3294 | }; | |
| 2877 | 3295 | |
| 2878 | const mir_tag: Mir.Inst.Tag = switch (tag) { | |
| 2879 | .add => .add, | |
| 2880 | .sub => .sub, | |
| 2881 | .cmp_eq => .cmp, | |
| 2882 | else => unreachable, | |
| 2883 | }; | |
| 3296 | const mir_tag: Mir.Inst.Tag = switch (tag) { | |
| 3297 | .add => .add, | |
| 3298 | .sub => .sub, | |
| 3299 | else => unreachable, | |
| 3300 | }; | |
| 2884 | 3301 | |
| 2885 | if (rhs_immediate_ok) { | |
| 2886 | return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); | |
| 2887 | } else if (lhs_immediate_ok) { | |
| 2888 | // swap lhs and rhs | |
| 2889 | return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); | |
| 2890 | } else { | |
| 2891 | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | |
| 2892 | } | |
| 2893 | } else { | |
| 2894 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 2895 | } | |
| 2896 | }, | |
| 2897 | else => unreachable, | |
| 2898 | } | |
| 2899 | }, | |
| 2900 | .mul => { | |
| 2901 | switch (lhs_ty.zigTypeTag()) { | |
| 2902 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 2903 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 2904 | .Int => { | |
| 2905 | const mod = self.bin_file.options.module.?; | |
| 2906 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 2907 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 2908 | if (int_info.bits <= 32) { | |
| 2909 | // TODO add optimisations for multiplication | |
| 2910 | // with immediates, for example a * 2 can be | |
| 2911 | // lowered to a << 1 | |
| 2912 | return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata); | |
| 2913 | } else { | |
| 2914 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 2915 | } | |
| 2916 | }, | |
| 2917 | else => unreachable, | |
| 3302 | if (rhs_immediate_ok) { | |
| 3303 | return try self.binOpImmediateNew(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); | |
| 3304 | } else if (lhs_immediate_ok) { | |
| 3305 | // swap lhs and rhs | |
| 3306 | return try self.binOpImmediateNew(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); | |
| 3307 | } else { | |
| 3308 | return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); | |
| 3309 | } | |
| 3310 | } else { | |
| 3311 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 2918 | 3312 | } |
| 2919 | 3313 | }, |
| 2920 | .div_float => { | |
| 2921 | switch (lhs_ty.zigTypeTag()) { | |
| 2922 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 2923 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 2924 | else => unreachable, | |
| 3314 | else => unreachable, | |
| 3315 | } | |
| 3316 | } | |
| 3317 | ||
| 3318 | fn mul( | |
| 3319 | self: *Self, | |
| 3320 | lhs_bind: ReadArg.Bind, | |
| 3321 | rhs_bind: ReadArg.Bind, | |
| 3322 | lhs_ty: Type, | |
| 3323 | rhs_ty: Type, | |
| 3324 | maybe_inst: ?Air.Inst.Index, | |
| 3325 | ) InnerError!MCValue { | |
| 3326 | switch (lhs_ty.zigTypeTag()) { | |
| 3327 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 3328 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3329 | .Int => { | |
| 3330 | const mod = self.bin_file.options.module.?; | |
| 3331 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 3332 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3333 | if (int_info.bits <= 32) { | |
| 3334 | // TODO add optimisations for multiplication | |
| 3335 | // with immediates, for example a * 2 can be | |
| 3336 | // lowered to a << 1 | |
| 3337 | return try self.binOpRegisterNew(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); | |
| 3338 | } else { | |
| 3339 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 2925 | 3340 | } |
| 2926 | 3341 | }, |
| 2927 | .div_trunc, .div_floor => { | |
| 2928 | switch (lhs_ty.zigTypeTag()) { | |
| 2929 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 2930 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 2931 | .Int => { | |
| 2932 | const mod = self.bin_file.options.module.?; | |
| 2933 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 2934 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 2935 | if (int_info.bits <= 32) { | |
| 2936 | switch (int_info.signedness) { | |
| 2937 | .signed => { | |
| 2938 | return self.fail("TODO ARM signed integer division", .{}); | |
| 2939 | }, | |
| 2940 | .unsigned => { | |
| 2941 | switch (rhs) { | |
| 2942 | .immediate => |imm| { | |
| 2943 | if (std.math.isPowerOfTwo(imm)) { | |
| 2944 | const shift = MCValue{ .immediate = std.math.log2_int(u32, imm) }; | |
| 2945 | return try self.binOp(.shr, lhs, shift, lhs_ty, rhs_ty, metadata); | |
| 2946 | } else { | |
| 2947 | return self.fail("TODO ARM integer division by constants", .{}); | |
| 2948 | } | |
| 2949 | }, | |
| 2950 | else => return self.fail("TODO ARM integer division", .{}), | |
| 2951 | } | |
| 2952 | }, | |
| 3342 | else => unreachable, | |
| 3343 | } | |
| 3344 | } | |
| 3345 | ||
| 3346 | fn divFloat( | |
| 3347 | self: *Self, | |
| 3348 | lhs_bind: ReadArg.Bind, | |
| 3349 | rhs_bind: ReadArg.Bind, | |
| 3350 | lhs_ty: Type, | |
| 3351 | rhs_ty: Type, | |
| 3352 | maybe_inst: ?Air.Inst.Index, | |
| 3353 | ) InnerError!MCValue { | |
| 3354 | _ = lhs_bind; | |
| 3355 | _ = rhs_bind; | |
| 3356 | _ = lhs_ty; | |
| 3357 | _ = rhs_ty; | |
| 3358 | _ = maybe_inst; | |
| 3359 | ||
| 3360 | switch (lhs_ty.zigTypeTag()) { | |
| 3361 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 3362 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3363 | else => unreachable, | |
| 3364 | } | |
| 3365 | } | |
| 3366 | ||
| 3367 | fn div( | |
| 3368 | self: *Self, | |
| 3369 | tag: Air.Inst.Tag, | |
| 3370 | lhs_bind: ReadArg.Bind, | |
| 3371 | rhs_bind: ReadArg.Bind, | |
| 3372 | lhs_ty: Type, | |
| 3373 | rhs_ty: Type, | |
| 3374 | maybe_inst: ?Air.Inst.Index, | |
| 3375 | ) InnerError!MCValue { | |
| 3376 | _ = tag; | |
| 3377 | ||
| 3378 | switch (lhs_ty.zigTypeTag()) { | |
| 3379 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 3380 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3381 | .Int => { | |
| 3382 | const mod = self.bin_file.options.module.?; | |
| 3383 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 3384 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3385 | if (int_info.bits <= 32) { | |
| 3386 | switch (int_info.signedness) { | |
| 3387 | .signed => { | |
| 3388 | return self.fail("TODO ARM signed integer division", .{}); | |
| 3389 | }, | |
| 3390 | .unsigned => { | |
| 3391 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); | |
| 3392 | ||
| 3393 | if (rhs_immediate) |imm| { | |
| 3394 | if (std.math.isPowerOfTwo(imm)) { | |
| 3395 | const shift = std.math.log2_int(u32, imm); | |
| 3396 | return try self.binOpImmediateNew(.lsr, lhs_bind, shift, lhs_ty, false, maybe_inst); | |
| 3397 | } else { | |
| 3398 | return self.fail("TODO ARM integer division by constants", .{}); | |
| 3399 | } | |
| 3400 | } else { | |
| 3401 | return self.fail("TODO ARM integer division", .{}); | |
| 2953 | 3402 | } |
| 2954 | } else { | |
| 2955 | return self.fail("TODO ARM integer division for integers > u32/i32", .{}); | |
| 2956 | } | |
| 2957 | }, | |
| 2958 | else => unreachable, | |
| 2959 | } | |
| 2960 | }, | |
| 2961 | .div_exact => { | |
| 2962 | switch (lhs_ty.zigTypeTag()) { | |
| 2963 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 2964 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 2965 | .Int => return self.fail("TODO ARM div_exact", .{}), | |
| 2966 | else => unreachable, | |
| 3403 | }, | |
| 3404 | } | |
| 3405 | } else { | |
| 3406 | return self.fail("TODO ARM integer division for integers > u32/i32", .{}); | |
| 2967 | 3407 | } |
| 2968 | 3408 | }, |
| 2969 | .rem => { | |
| 2970 | switch (lhs_ty.zigTypeTag()) { | |
| 2971 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 2972 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 2973 | .Int => { | |
| 2974 | const mod = self.bin_file.options.module.?; | |
| 2975 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 2976 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 2977 | if (int_info.bits <= 32) { | |
| 2978 | switch (int_info.signedness) { | |
| 2979 | .signed => { | |
| 2980 | return self.fail("TODO ARM signed integer mod", .{}); | |
| 2981 | }, | |
| 2982 | .unsigned => { | |
| 2983 | switch (rhs) { | |
| 2984 | .immediate => |imm| { | |
| 2985 | if (std.math.isPowerOfTwo(imm)) { | |
| 2986 | const log2 = std.math.log2_int(u32, imm); | |
| 2987 | ||
| 2988 | const lhs_is_register = lhs == .register; | |
| 2989 | ||
| 2990 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | |
| 2991 | self.register_manager.lockReg(lhs.register) | |
| 2992 | else | |
| 2993 | null; | |
| 2994 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 2995 | ||
| 2996 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | |
| 2997 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | |
| 2998 | break :inst Air.refToIndex(md.lhs).?; | |
| 2999 | } else null; | |
| 3000 | ||
| 3001 | break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); | |
| 3002 | }; | |
| 3003 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | |
| 3004 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | |
| 3005 | ||
| 3006 | const dest_reg = if (metadata) |md| blk: { | |
| 3007 | if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { | |
| 3008 | break :blk lhs_reg; | |
| 3009 | } else { | |
| 3010 | break :blk try self.register_manager.allocReg(md.inst, gp); | |
| 3011 | } | |
| 3012 | } else try self.register_manager.allocReg(null, gp); | |
| 3013 | ||
| 3014 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | |
| 3015 | ||
| 3016 | try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2); | |
| 3017 | return MCValue{ .register = dest_reg }; | |
| 3018 | } else { | |
| 3019 | return self.fail("TODO ARM integer mod by constants", .{}); | |
| 3020 | } | |
| 3021 | }, | |
| 3022 | else => return self.fail("TODO ARM integer mod", .{}), | |
| 3023 | } | |
| 3024 | }, | |
| 3409 | else => unreachable, | |
| 3410 | } | |
| 3411 | } | |
| 3412 | ||
| 3413 | fn divExact( | |
| 3414 | self: *Self, | |
| 3415 | lhs_bind: ReadArg.Bind, | |
| 3416 | rhs_bind: ReadArg.Bind, | |
| 3417 | lhs_ty: Type, | |
| 3418 | rhs_ty: Type, | |
| 3419 | maybe_inst: ?Air.Inst.Index, | |
| 3420 | ) InnerError!MCValue { | |
| 3421 | _ = lhs_bind; | |
| 3422 | _ = rhs_bind; | |
| 3423 | _ = lhs_ty; | |
| 3424 | _ = rhs_ty; | |
| 3425 | _ = maybe_inst; | |
| 3426 | ||
| 3427 | switch (lhs_ty.zigTypeTag()) { | |
| 3428 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 3429 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3430 | .Int => return self.fail("TODO ARM div_exact", .{}), | |
| 3431 | else => unreachable, | |
| 3432 | } | |
| 3433 | } | |
| 3434 | ||
| 3435 | fn rem( | |
| 3436 | self: *Self, | |
| 3437 | lhs_bind: ReadArg.Bind, | |
| 3438 | rhs_bind: ReadArg.Bind, | |
| 3439 | lhs_ty: Type, | |
| 3440 | rhs_ty: Type, | |
| 3441 | maybe_inst: ?Air.Inst.Index, | |
| 3442 | ) InnerError!MCValue { | |
| 3443 | switch (lhs_ty.zigTypeTag()) { | |
| 3444 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 3445 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3446 | .Int => { | |
| 3447 | const mod = self.bin_file.options.module.?; | |
| 3448 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 3449 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3450 | if (int_info.bits <= 32) { | |
| 3451 | switch (int_info.signedness) { | |
| 3452 | .signed => { | |
| 3453 | return self.fail("TODO ARM signed integer mod", .{}); | |
| 3454 | }, | |
| 3455 | .unsigned => { | |
| 3456 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); | |
| 3457 | ||
| 3458 | if (rhs_immediate) |imm| { | |
| 3459 | if (std.math.isPowerOfTwo(imm)) { | |
| 3460 | const log2 = std.math.log2_int(u32, imm); | |
| 3461 | ||
| 3462 | var lhs_reg: Register = undefined; | |
| 3463 | var dest_reg: Register = undefined; | |
| 3464 | ||
| 3465 | const read_args = [_]ReadArg{ | |
| 3466 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, | |
| 3467 | }; | |
| 3468 | const write_args = [_]WriteArg{ | |
| 3469 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, | |
| 3470 | }; | |
| 3471 | try self.allocRegs( | |
| 3472 | &read_args, | |
| 3473 | &write_args, | |
| 3474 | if (maybe_inst) |inst| .{ | |
| 3475 | .corresponding_inst = inst, | |
| 3476 | .operand_mapping = &.{0}, | |
| 3477 | } else null, | |
| 3478 | ); | |
| 3479 | ||
| 3480 | try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2); | |
| 3481 | ||
| 3482 | return MCValue{ .register = dest_reg }; | |
| 3483 | } else { | |
| 3484 | return self.fail("TODO ARM integer mod by constants", .{}); | |
| 3485 | } | |
| 3486 | } else { | |
| 3487 | return self.fail("TODO ARM integer mod", .{}); | |
| 3025 | 3488 | } |
| 3026 | } else { | |
| 3027 | return self.fail("TODO ARM integer division for integers > u32/i32", .{}); | |
| 3028 | } | |
| 3029 | }, | |
| 3030 | else => unreachable, | |
| 3489 | }, | |
| 3490 | } | |
| 3491 | } else { | |
| 3492 | return self.fail("TODO ARM integer division for integers > u32/i32", .{}); | |
| 3031 | 3493 | } |
| 3032 | 3494 | }, |
| 3033 | .mod => { | |
| 3034 | switch (lhs_ty.zigTypeTag()) { | |
| 3035 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 3036 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3037 | .Int => return self.fail("TODO ARM mod", .{}), | |
| 3038 | else => unreachable, | |
| 3495 | else => unreachable, | |
| 3496 | } | |
| 3497 | } | |
| 3498 | ||
| 3499 | fn modulo( | |
| 3500 | self: *Self, | |
| 3501 | lhs_bind: ReadArg.Bind, | |
| 3502 | rhs_bind: ReadArg.Bind, | |
| 3503 | lhs_ty: Type, | |
| 3504 | rhs_ty: Type, | |
| 3505 | maybe_inst: ?Air.Inst.Index, | |
| 3506 | ) InnerError!MCValue { | |
| 3507 | _ = lhs_bind; | |
| 3508 | _ = rhs_bind; | |
| 3509 | _ = lhs_ty; | |
| 3510 | _ = rhs_ty; | |
| 3511 | _ = maybe_inst; | |
| 3512 | ||
| 3513 | switch (lhs_ty.zigTypeTag()) { | |
| 3514 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 3515 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3516 | .Int => return self.fail("TODO ARM mod", .{}), | |
| 3517 | else => unreachable, | |
| 3518 | } | |
| 3519 | } | |
| 3520 | ||
| 3521 | fn wrappingArithmetic( | |
| 3522 | self: *Self, | |
| 3523 | tag: Air.Inst.Tag, | |
| 3524 | lhs_bind: ReadArg.Bind, | |
| 3525 | rhs_bind: ReadArg.Bind, | |
| 3526 | lhs_ty: Type, | |
| 3527 | rhs_ty: Type, | |
| 3528 | maybe_inst: ?Air.Inst.Index, | |
| 3529 | ) InnerError!MCValue { | |
| 3530 | const base_tag: Air.Inst.Tag = switch (tag) { | |
| 3531 | .addwrap => .add, | |
| 3532 | .subwrap => .sub, | |
| 3533 | .mulwrap => .mul, | |
| 3534 | else => unreachable, | |
| 3535 | }; | |
| 3536 | ||
| 3537 | // Generate an add/sub/mul | |
| 3538 | const result = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); | |
| 3539 | ||
| 3540 | // Truncate if necessary | |
| 3541 | switch (lhs_ty.zigTypeTag()) { | |
| 3542 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3543 | .Int => { | |
| 3544 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3545 | if (int_info.bits <= 32) { | |
| 3546 | const result_reg = result.register; | |
| 3547 | ||
| 3548 | if (int_info.bits < 32) { | |
| 3549 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); | |
| 3550 | return result; | |
| 3551 | } else return result; | |
| 3552 | } else { | |
| 3553 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 3039 | 3554 | } |
| 3040 | 3555 | }, |
| 3041 | .addwrap, | |
| 3042 | .subwrap, | |
| 3043 | .mulwrap, | |
| 3044 | => { | |
| 3045 | const base_tag: Air.Inst.Tag = switch (tag) { | |
| 3046 | .addwrap => .add, | |
| 3047 | .subwrap => .sub, | |
| 3048 | .mulwrap => .mul, | |
| 3049 | else => unreachable, | |
| 3050 | }; | |
| 3556 | else => unreachable, | |
| 3557 | } | |
| 3558 | } | |
| 3559 | ||
| 3560 | fn bitwise( | |
| 3561 | self: *Self, | |
| 3562 | tag: Air.Inst.Tag, | |
| 3563 | lhs_bind: ReadArg.Bind, | |
| 3564 | rhs_bind: ReadArg.Bind, | |
| 3565 | lhs_ty: Type, | |
| 3566 | rhs_ty: Type, | |
| 3567 | maybe_inst: ?Air.Inst.Index, | |
| 3568 | ) InnerError!MCValue { | |
| 3569 | switch (lhs_ty.zigTypeTag()) { | |
| 3570 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3571 | .Int => { | |
| 3572 | const mod = self.bin_file.options.module.?; | |
| 3573 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 3574 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3575 | if (int_info.bits <= 32) { | |
| 3576 | const lhs_immediate = try lhs_bind.resolveToImmediate(self); | |
| 3577 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); | |
| 3578 | ||
| 3579 | const lhs_immediate_ok = if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false; | |
| 3580 | const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false; | |
| 3581 | ||
| 3582 | const mir_tag: Mir.Inst.Tag = switch (tag) { | |
| 3583 | .bit_and => .@"and", | |
| 3584 | .bit_or => .orr, | |
| 3585 | .xor => .eor, | |
| 3586 | else => unreachable, | |
| 3587 | }; | |
| 3051 | 3588 | |
| 3052 | // Generate an add/sub/mul | |
| 3053 | const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | |
| 3054 | ||
| 3055 | // Truncate if necessary | |
| 3056 | switch (lhs_ty.zigTypeTag()) { | |
| 3057 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3058 | .Int => { | |
| 3059 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3060 | if (int_info.bits <= 32) { | |
| 3061 | const result_reg = result.register; | |
| 3062 | ||
| 3063 | if (int_info.bits < 32) { | |
| 3064 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); | |
| 3065 | return result; | |
| 3066 | } else return result; | |
| 3067 | } else { | |
| 3068 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 3069 | } | |
| 3070 | }, | |
| 3071 | else => unreachable, | |
| 3589 | if (rhs_immediate_ok) { | |
| 3590 | return try self.binOpImmediateNew(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); | |
| 3591 | } else if (lhs_immediate_ok) { | |
| 3592 | // swap lhs and rhs | |
| 3593 | return try self.binOpImmediateNew(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); | |
| 3594 | } else { | |
| 3595 | return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); | |
| 3596 | } | |
| 3597 | } else { | |
| 3598 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 3072 | 3599 | } |
| 3073 | 3600 | }, |
| 3074 | .bit_and, | |
| 3075 | .bit_or, | |
| 3076 | .xor, | |
| 3077 | => { | |
| 3078 | switch (lhs_ty.zigTypeTag()) { | |
| 3079 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3080 | .Int => { | |
| 3081 | const mod = self.bin_file.options.module.?; | |
| 3082 | assert(lhs_ty.eql(rhs_ty, mod)); | |
| 3083 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3084 | if (int_info.bits <= 32) { | |
| 3085 | const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null; | |
| 3086 | const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null; | |
| 3087 | ||
| 3088 | const mir_tag: Mir.Inst.Tag = switch (tag) { | |
| 3089 | .bit_and => .@"and", | |
| 3090 | .bit_or => .orr, | |
| 3091 | .xor => .eor, | |
| 3092 | else => unreachable, | |
| 3093 | }; | |
| 3601 | else => unreachable, | |
| 3602 | } | |
| 3603 | } | |
| 3094 | 3604 | |
| 3095 | if (rhs_immediate_ok) { | |
| 3096 | return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); | |
| 3097 | } else if (lhs_immediate_ok) { | |
| 3098 | // swap lhs and rhs | |
| 3099 | return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); | |
| 3100 | } else { | |
| 3101 | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | |
| 3102 | } | |
| 3103 | } else { | |
| 3104 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 3105 | } | |
| 3106 | }, | |
| 3107 | else => unreachable, | |
| 3108 | } | |
| 3109 | }, | |
| 3110 | .shl_exact, | |
| 3111 | .shr_exact, | |
| 3112 | => { | |
| 3113 | switch (lhs_ty.zigTypeTag()) { | |
| 3114 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3115 | .Int => { | |
| 3116 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3117 | if (int_info.bits <= 32) { | |
| 3118 | const rhs_immediate_ok = rhs == .immediate; | |
| 3119 | ||
| 3120 | const mir_tag: Mir.Inst.Tag = switch (tag) { | |
| 3121 | .shl_exact => .lsl, | |
| 3122 | .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) { | |
| 3123 | .signed => Mir.Inst.Tag.asr, | |
| 3124 | .unsigned => Mir.Inst.Tag.lsr, | |
| 3125 | }, | |
| 3126 | else => unreachable, | |
| 3127 | }; | |
| 3605 | fn shiftExact( | |
| 3606 | self: *Self, | |
| 3607 | tag: Air.Inst.Tag, | |
| 3608 | lhs_bind: ReadArg.Bind, | |
| 3609 | rhs_bind: ReadArg.Bind, | |
| 3610 | lhs_ty: Type, | |
| 3611 | rhs_ty: Type, | |
| 3612 | maybe_inst: ?Air.Inst.Index, | |
| 3613 | ) InnerError!MCValue { | |
| 3614 | switch (lhs_ty.zigTypeTag()) { | |
| 3615 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3616 | .Int => { | |
| 3617 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3618 | if (int_info.bits <= 32) { | |
| 3619 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); | |
| 3128 | 3620 | |
| 3129 | if (rhs_immediate_ok) { | |
| 3130 | return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); | |
| 3131 | } else { | |
| 3132 | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | |
| 3133 | } | |
| 3134 | } else { | |
| 3135 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 3136 | } | |
| 3137 | }, | |
| 3138 | else => unreachable, | |
| 3621 | const mir_tag: Mir.Inst.Tag = switch (tag) { | |
| 3622 | .shl_exact => .lsl, | |
| 3623 | .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) { | |
| 3624 | .signed => Mir.Inst.Tag.asr, | |
| 3625 | .unsigned => Mir.Inst.Tag.lsr, | |
| 3626 | }, | |
| 3627 | else => unreachable, | |
| 3628 | }; | |
| 3629 | ||
| 3630 | if (rhs_immediate) |imm| { | |
| 3631 | return try self.binOpImmediateNew(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst); | |
| 3632 | } else { | |
| 3633 | return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); | |
| 3634 | } | |
| 3635 | } else { | |
| 3636 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 3139 | 3637 | } |
| 3140 | 3638 | }, |
| 3141 | .shl, | |
| 3142 | .shr, | |
| 3143 | => { | |
| 3144 | const base_tag: Air.Inst.Tag = switch (tag) { | |
| 3145 | .shl => .shl_exact, | |
| 3146 | .shr => .shr_exact, | |
| 3147 | else => unreachable, | |
| 3148 | }; | |
| 3639 | else => unreachable, | |
| 3640 | } | |
| 3641 | } | |
| 3642 | ||
| 3643 | fn shiftNormal( | |
| 3644 | self: *Self, | |
| 3645 | tag: Air.Inst.Tag, | |
| 3646 | lhs_bind: ReadArg.Bind, | |
| 3647 | rhs_bind: ReadArg.Bind, | |
| 3648 | lhs_ty: Type, | |
| 3649 | rhs_ty: Type, | |
| 3650 | maybe_inst: ?Air.Inst.Index, | |
| 3651 | ) InnerError!MCValue { | |
| 3652 | const base_tag: Air.Inst.Tag = switch (tag) { | |
| 3653 | .shl => .shl_exact, | |
| 3654 | .shr => .shr_exact, | |
| 3655 | else => unreachable, | |
| 3656 | }; | |
| 3149 | 3657 | |
| 3150 | // Generate a shl_exact/shr_exact | |
| 3151 | const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | |
| 3658 | // Generate a shl_exact/shr_exact | |
| 3659 | const result = try self.shiftExact(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); | |
| 3152 | 3660 | |
| 3153 | // Truncate if necessary | |
| 3154 | switch (tag) { | |
| 3155 | .shr => return result, | |
| 3156 | .shl => switch (lhs_ty.zigTypeTag()) { | |
| 3157 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3158 | .Int => { | |
| 3159 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3160 | if (int_info.bits <= 32) { | |
| 3161 | const result_reg = result.register; | |
| 3661 | // Truncate if necessary | |
| 3662 | switch (tag) { | |
| 3663 | .shr => return result, | |
| 3664 | .shl => switch (lhs_ty.zigTypeTag()) { | |
| 3665 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 3666 | .Int => { | |
| 3667 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 3668 | if (int_info.bits <= 32) { | |
| 3669 | const result_reg = result.register; | |
| 3162 | 3670 | |
| 3163 | if (int_info.bits < 32) { | |
| 3164 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); | |
| 3165 | return result; | |
| 3166 | } else return result; | |
| 3167 | } else { | |
| 3168 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 3169 | } | |
| 3170 | }, | |
| 3171 | else => unreachable, | |
| 3172 | }, | |
| 3173 | else => unreachable, | |
| 3174 | } | |
| 3671 | if (int_info.bits < 32) { | |
| 3672 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); | |
| 3673 | return result; | |
| 3674 | } else return result; | |
| 3675 | } else { | |
| 3676 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 3677 | } | |
| 3678 | }, | |
| 3679 | else => unreachable, | |
| 3175 | 3680 | }, |
| 3176 | .bool_and, | |
| 3177 | .bool_or, | |
| 3178 | => { | |
| 3179 | switch (lhs_ty.zigTypeTag()) { | |
| 3180 | .Bool => { | |
| 3181 | const lhs_immediate_ok = lhs == .immediate; | |
| 3182 | const rhs_immediate_ok = rhs == .immediate; | |
| 3681 | else => unreachable, | |
| 3682 | } | |
| 3683 | } | |
| 3183 | 3684 | |
| 3184 | const mir_tag: Mir.Inst.Tag = switch (tag) { | |
| 3185 | .bool_and => .@"and", | |
| 3186 | .bool_or => .orr, | |
| 3187 | else => unreachable, | |
| 3188 | }; | |
| 3685 | fn booleanOp( | |
| 3686 | self: *Self, | |
| 3687 | tag: Air.Inst.Tag, | |
| 3688 | lhs_bind: ReadArg.Bind, | |
| 3689 | rhs_bind: ReadArg.Bind, | |
| 3690 | lhs_ty: Type, | |
| 3691 | rhs_ty: Type, | |
| 3692 | maybe_inst: ?Air.Inst.Index, | |
| 3693 | ) InnerError!MCValue { | |
| 3694 | switch (lhs_ty.zigTypeTag()) { | |
| 3695 | .Bool => { | |
| 3696 | const lhs_immediate = try lhs_bind.resolveToImmediate(self); | |
| 3697 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); | |
| 3189 | 3698 | |
| 3190 | if (rhs_immediate_ok) { | |
| 3191 | return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); | |
| 3192 | } else if (lhs_immediate_ok) { | |
| 3193 | // swap lhs and rhs | |
| 3194 | return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); | |
| 3195 | } else { | |
| 3196 | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | |
| 3197 | } | |
| 3198 | }, | |
| 3699 | const mir_tag: Mir.Inst.Tag = switch (tag) { | |
| 3700 | .bool_and => .@"and", | |
| 3701 | .bool_or => .orr, | |
| 3199 | 3702 | else => unreachable, |
| 3703 | }; | |
| 3704 | ||
| 3705 | if (rhs_immediate) |imm| { | |
| 3706 | return try self.binOpImmediateNew(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst); | |
| 3707 | } else if (lhs_immediate) |imm| { | |
| 3708 | // swap lhs and rhs | |
| 3709 | return try self.binOpImmediateNew(mir_tag, rhs_bind, imm, rhs_ty, true, maybe_inst); | |
| 3710 | } else { | |
| 3711 | return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); | |
| 3200 | 3712 | } |
| 3201 | 3713 | }, |
| 3202 | .ptr_add, | |
| 3203 | .ptr_sub, | |
| 3204 | => { | |
| 3205 | switch (lhs_ty.zigTypeTag()) { | |
| 3206 | .Pointer => { | |
| 3207 | const ptr_ty = lhs_ty; | |
| 3208 | const elem_ty = switch (ptr_ty.ptrSize()) { | |
| 3209 | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type | |
| 3210 | else => ptr_ty.childType(), | |
| 3211 | }; | |
| 3212 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); | |
| 3714 | else => unreachable, | |
| 3715 | } | |
| 3716 | } | |
| 3213 | 3717 | |
| 3214 | if (elem_size == 1) { | |
| 3215 | const base_tag: Mir.Inst.Tag = switch (tag) { | |
| 3216 | .ptr_add => .add, | |
| 3217 | .ptr_sub => .sub, | |
| 3218 | else => unreachable, | |
| 3219 | }; | |
| 3718 | fn ptrArithmetic( | |
| 3719 | self: *Self, | |
| 3720 | tag: Air.Inst.Tag, | |
| 3721 | lhs_bind: ReadArg.Bind, | |
| 3722 | rhs_bind: ReadArg.Bind, | |
| 3723 | lhs_ty: Type, | |
| 3724 | rhs_ty: Type, | |
| 3725 | maybe_inst: ?Air.Inst.Index, | |
| 3726 | ) InnerError!MCValue { | |
| 3727 | switch (lhs_ty.zigTypeTag()) { | |
| 3728 | .Pointer => { | |
| 3729 | const mod = self.bin_file.options.module.?; | |
| 3730 | assert(rhs_ty.eql(Type.usize, mod)); | |
| 3220 | 3731 | |
| 3221 | return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | |
| 3222 | } else { | |
| 3223 | // convert the offset into a byte offset by | |
| 3224 | // multiplying it with elem_size | |
| 3225 | const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null); | |
| 3226 | const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null); | |
| 3227 | return addr; | |
| 3228 | } | |
| 3229 | }, | |
| 3732 | const ptr_ty = lhs_ty; | |
| 3733 | const elem_ty = switch (ptr_ty.ptrSize()) { | |
| 3734 | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type | |
| 3735 | else => ptr_ty.childType(), | |
| 3736 | }; | |
| 3737 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); | |
| 3738 | ||
| 3739 | const base_tag: Air.Inst.Tag = switch (tag) { | |
| 3740 | .ptr_add => .add, | |
| 3741 | .ptr_sub => .sub, | |
| 3230 | 3742 | else => unreachable, |
| 3743 | }; | |
| 3744 | ||
| 3745 | if (elem_size == 1) { | |
| 3746 | return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst); | |
| 3747 | } else { | |
| 3748 | // convert the offset into a byte offset by | |
| 3749 | // multiplying it with elem_size | |
| 3750 | const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } }; | |
| 3751 | ||
| 3752 | const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null); | |
| 3753 | const offset_bind = ReadArg.Bind{ .mcv = offset }; | |
| 3754 | ||
| 3755 | const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null); | |
| 3756 | return addr; | |
| 3231 | 3757 | } |
| 3232 | 3758 | }, |
| 3233 | 3759 | else => unreachable, |
| ... | ... | @@ -3312,9 +3838,8 @@ fn genInlineMemcpy( |
| 3312 | 3838 | // mov count, #0 |
| 3313 | 3839 | _ = try self.addInst(.{ |
| 3314 | 3840 | .tag = .mov, |
| 3315 | .data = .{ .rr_op = .{ | |
| 3841 | .data = .{ .r_op_mov = .{ | |
| 3316 | 3842 | .rd = count, |
| 3317 | .rn = .r0, | |
| 3318 | 3843 | .op = Instruction.Operand.imm(0, 0), |
| 3319 | 3844 | } }, |
| 3320 | 3845 | }); |
| ... | ... | @@ -3323,8 +3848,7 @@ fn genInlineMemcpy( |
| 3323 | 3848 | // cmp count, len |
| 3324 | 3849 | _ = try self.addInst(.{ |
| 3325 | 3850 | .tag = .cmp, |
| 3326 | .data = .{ .rr_op = .{ | |
| 3327 | .rd = .r0, | |
| 3851 | .data = .{ .r_op_cmp = .{ | |
| 3328 | 3852 | .rn = count, |
| 3329 | 3853 | .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none), |
| 3330 | 3854 | } }, |
| ... | ... | @@ -3418,9 +3942,8 @@ fn genInlineMemsetCode( |
| 3418 | 3942 | // mov count, #0 |
| 3419 | 3943 | _ = try self.addInst(.{ |
| 3420 | 3944 | .tag = .mov, |
| 3421 | .data = .{ .rr_op = .{ | |
| 3945 | .data = .{ .r_op_mov = .{ | |
| 3422 | 3946 | .rd = count, |
| 3423 | .rn = .r0, | |
| 3424 | 3947 | .op = Instruction.Operand.imm(0, 0), |
| 3425 | 3948 | } }, |
| 3426 | 3949 | }); |
| ... | ... | @@ -3429,8 +3952,7 @@ fn genInlineMemsetCode( |
| 3429 | 3952 | // cmp count, len |
| 3430 | 3953 | _ = try self.addInst(.{ |
| 3431 | 3954 | .tag = .cmp, |
| 3432 | .data = .{ .rr_op = .{ | |
| 3433 | .rd = .r0, | |
| 3955 | .data = .{ .r_op_cmp = .{ | |
| 3434 | 3956 | .rn = count, |
| 3435 | 3957 | .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none), |
| 3436 | 3958 | } }, |
| ... | ... | @@ -3568,7 +4090,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 3568 | 4090 | .register => |reg| blk: { |
| 3569 | 4091 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 3570 | 4092 | const abi_align = ty.abiAlignment(self.target.*); |
| 3571 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); | |
| 4093 | const stack_offset = try self.allocMem(abi_size, abi_align, inst); | |
| 3572 | 4094 | try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 3573 | 4095 | |
| 3574 | 4096 | break :blk MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -3655,7 +4177,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3655 | 4177 | const ret_ty = fn_ty.fnReturnType(); |
| 3656 | 4178 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 3657 | 4179 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); |
| 3658 | const stack_offset = try self.allocMem(inst, ret_abi_size, ret_abi_align); | |
| 4180 | const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst); | |
| 3659 | 4181 | |
| 3660 | 4182 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| 3661 | 4183 | .base = .{ .tag = .single_mut_pointer }, |
| ... | ... | @@ -3843,14 +4365,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3843 | 4365 | const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 3844 | 4366 | const abi_align = ret_ty.abiAlignment(self.target.*); |
| 3845 | 4367 | |
| 3846 | // This is essentially allocMem without the | |
| 3847 | // instruction tracking | |
| 3848 | if (abi_align > self.stack_align) | |
| 3849 | self.stack_align = abi_align; | |
| 3850 | // TODO find a free slot instead of always appending | |
| 3851 | const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; | |
| 3852 | self.next_stack_offset = offset; | |
| 3853 | self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); | |
| 4368 | const offset = try self.allocMem(abi_size, abi_align, null); | |
| 3854 | 4369 | |
| 3855 | 4370 | const tmp_mcv = MCValue{ .stack_offset = offset }; |
| 3856 | 4371 | try self.load(tmp_mcv, ptr, ptr_ty); |
| ... | ... | @@ -3871,32 +4386,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3871 | 4386 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 3872 | 4387 | |
| 3873 | 4388 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { |
| 3874 | const operands: BinOpOperands = .{ .inst = .{ | |
| 3875 | .inst = inst, | |
| 3876 | .lhs = bin_op.lhs, | |
| 3877 | .rhs = bin_op.rhs, | |
| 3878 | } }; | |
| 3879 | break :blk try self.cmp(operands, lhs_ty, op); | |
| 4389 | break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op); | |
| 3880 | 4390 | }; |
| 3881 | 4391 | |
| 3882 | 4392 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3883 | 4393 | } |
| 3884 | 4394 | |
| 3885 | const BinOpOperands = union(enum) { | |
| 3886 | inst: struct { | |
| 3887 | inst: Air.Inst.Index, | |
| 3888 | lhs: Air.Inst.Ref, | |
| 3889 | rhs: Air.Inst.Ref, | |
| 3890 | }, | |
| 3891 | mcv: struct { | |
| 3892 | lhs: MCValue, | |
| 3893 | rhs: MCValue, | |
| 3894 | }, | |
| 3895 | }; | |
| 3896 | ||
| 3897 | 4395 | fn cmp( |
| 3898 | 4396 | self: *Self, |
| 3899 | operands: BinOpOperands, | |
| 4397 | lhs: ReadArg.Bind, | |
| 4398 | rhs: ReadArg.Bind, | |
| 3900 | 4399 | lhs_ty: Type, |
| 3901 | 4400 | op: math.CompareOperator, |
| 3902 | 4401 | ) !MCValue { |
| ... | ... | @@ -3926,22 +4425,47 @@ fn cmp( |
| 3926 | 4425 | if (int_info.bits <= 32) { |
| 3927 | 4426 | try self.spillCompareFlagsIfOccupied(); |
| 3928 | 4427 | |
| 3929 | switch (operands) { | |
| 3930 | .inst => |inst_op| { | |
| 3931 | const metadata: BinOpMetadata = .{ | |
| 3932 | .inst = inst_op.inst, | |
| 3933 | .lhs = inst_op.lhs, | |
| 3934 | .rhs = inst_op.rhs, | |
| 3935 | }; | |
| 3936 | const lhs = try self.resolveInst(inst_op.lhs); | |
| 3937 | const rhs = try self.resolveInst(inst_op.rhs); | |
| 4428 | var lhs_reg: Register = undefined; | |
| 4429 | var rhs_reg: Register = undefined; | |
| 3938 | 4430 | |
| 3939 | self.cpsr_flags_inst = inst_op.inst; | |
| 3940 | _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, metadata); | |
| 3941 | }, | |
| 3942 | .mcv => |mcv_op| { | |
| 3943 | _ = try self.binOp(.cmp_eq, mcv_op.lhs, mcv_op.rhs, int_ty, int_ty, null); | |
| 3944 | }, | |
| 4431 | const rhs_immediate = try rhs.resolveToImmediate(self); | |
| 4432 | const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false; | |
| 4433 | ||
| 4434 | if (rhs_immediate_ok) { | |
| 4435 | const read_args = [_]ReadArg{ | |
| 4436 | .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, | |
| 4437 | }; | |
| 4438 | try self.allocRegs( | |
| 4439 | &read_args, | |
| 4440 | &.{}, | |
| 4441 | null, // we won't be able to reuse a register as there are no write_regs | |
| 4442 | ); | |
| 4443 | ||
| 4444 | _ = try self.addInst(.{ | |
| 4445 | .tag = .cmp, | |
| 4446 | .data = .{ .r_op_cmp = .{ | |
| 4447 | .rn = lhs_reg, | |
| 4448 | .op = Instruction.Operand.fromU32(rhs_immediate.?).?, | |
| 4449 | } }, | |
| 4450 | }); | |
| 4451 | } else { | |
| 4452 | const read_args = [_]ReadArg{ | |
| 4453 | .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, | |
| 4454 | .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg }, | |
| 4455 | }; | |
| 4456 | try self.allocRegs( | |
| 4457 | &read_args, | |
| 4458 | &.{}, | |
| 4459 | null, // we won't be able to reuse a register as there are no write_regs | |
| 4460 | ); | |
| 4461 | ||
| 4462 | _ = try self.addInst(.{ | |
| 4463 | .tag = .cmp, | |
| 4464 | .data = .{ .r_op_cmp = .{ | |
| 4465 | .rn = lhs_reg, | |
| 4466 | .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), | |
| 4467 | } }, | |
| 4468 | }); | |
| 3945 | 4469 | } |
| 3946 | 4470 | |
| 3947 | 4471 | return switch (int_info.signedness) { |
| ... | ... | @@ -4020,9 +4544,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { |
| 4020 | 4544 | // bne ... |
| 4021 | 4545 | _ = try self.addInst(.{ |
| 4022 | 4546 | .tag = .cmp, |
| 4023 | .cond = .al, | |
| 4024 | .data = .{ .rr_op = .{ | |
| 4025 | .rd = .r0, | |
| 4547 | .data = .{ .r_op_cmp = .{ | |
| 4026 | 4548 | .rn = reg, |
| 4027 | 4549 | .op = Instruction.Operand.imm(1, 0), |
| 4028 | 4550 | } }, |
| ... | ... | @@ -4132,7 +4654,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4132 | 4654 | if (else_value == .dead) |
| 4133 | 4655 | continue; |
| 4134 | 4656 | // The instruction is only overridden in the else branch. |
| 4135 | var i: usize = self.branch_stack.items.len - 2; | |
| 4657 | var i: usize = self.branch_stack.items.len - 1; | |
| 4136 | 4658 | while (true) { |
| 4137 | 4659 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? |
| 4138 | 4660 | if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { |
| ... | ... | @@ -4159,7 +4681,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4159 | 4681 | if (then_value == .dead) |
| 4160 | 4682 | continue; |
| 4161 | 4683 | const parent_mcv = blk: { |
| 4162 | var i: usize = self.branch_stack.items.len - 2; | |
| 4684 | var i: usize = self.branch_stack.items.len - 1; | |
| 4163 | 4685 | while (true) { |
| 4164 | 4686 | i -= 1; |
| 4165 | 4687 | if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { |
| ... | ... | @@ -4185,75 +4707,39 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4185 | 4707 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 4186 | 4708 | } |
| 4187 | 4709 | |
| 4188 | fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { | |
| 4189 | if (ty.isPtrLikeOptional()) { | |
| 4190 | assert(ty.abiSize(self.target.*) == 4); | |
| 4191 | ||
| 4192 | const reg_mcv: MCValue = switch (operand) { | |
| 4193 | .register => operand, | |
| 4194 | else => .{ .register = try self.copyToTmpRegister(ty, operand) }, | |
| 4195 | }; | |
| 4196 | ||
| 4197 | _ = try self.addInst(.{ | |
| 4198 | .tag = .cmp, | |
| 4199 | .data = .{ .rr_op = .{ | |
| 4200 | .rd = undefined, | |
| 4201 | .rn = reg_mcv.register, | |
| 4202 | .op = Instruction.Operand.fromU32(0).?, | |
| 4203 | } }, | |
| 4204 | }); | |
| 4710 | fn isNull( | |
| 4711 | self: *Self, | |
| 4712 | operand_bind: ReadArg.Bind, | |
| 4713 | operand_ty: Type, | |
| 4714 | ) !MCValue { | |
| 4715 | if (operand_ty.isPtrLikeOptional()) { | |
| 4716 | assert(operand_ty.abiSize(self.target.*) == 4); | |
| 4205 | 4717 | |
| 4206 | return MCValue{ .cpsr_flags = .eq }; | |
| 4718 | const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } }; | |
| 4719 | return self.cmp(operand_bind, imm_bind, Type.usize, .eq); | |
| 4207 | 4720 | } else { |
| 4208 | 4721 | return self.fail("TODO implement non-pointer optionals", .{}); |
| 4209 | 4722 | } |
| 4210 | 4723 | } |
| 4211 | 4724 | |
| 4212 | fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { | |
| 4213 | const is_null_result = try self.isNull(ty, operand); | |
| 4725 | fn isNonNull( | |
| 4726 | self: *Self, | |
| 4727 | operand_bind: ReadArg.Bind, | |
| 4728 | operand_ty: Type, | |
| 4729 | ) !MCValue { | |
| 4730 | const is_null_result = try self.isNull(operand_bind, operand_ty); | |
| 4214 | 4731 | assert(is_null_result.cpsr_flags == .eq); |
| 4215 | 4732 | |
| 4216 | 4733 | return MCValue{ .cpsr_flags = .ne }; |
| 4217 | 4734 | } |
| 4218 | 4735 | |
| 4219 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | |
| 4220 | const error_type = ty.errorUnionSet(); | |
| 4221 | const error_int_type = Type.initTag(.u16); | |
| 4222 | ||
| 4223 | if (error_type.errorSetIsEmpty()) { | |
| 4224 | return MCValue{ .immediate = 0 }; // always false | |
| 4225 | } | |
| 4226 | ||
| 4227 | const error_mcv = try self.errUnionErr(operand, ty); | |
| 4228 | _ = try self.binOp(.cmp_eq, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type, null); | |
| 4229 | return MCValue{ .cpsr_flags = .hi }; | |
| 4230 | } | |
| 4231 | ||
| 4232 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | |
| 4233 | const is_err_result = try self.isErr(ty, operand); | |
| 4234 | switch (is_err_result) { | |
| 4235 | .cpsr_flags => |cond| { | |
| 4236 | assert(cond == .hi); | |
| 4237 | return MCValue{ .cpsr_flags = cond.negate() }; | |
| 4238 | }, | |
| 4239 | .immediate => |imm| { | |
| 4240 | assert(imm == 0); | |
| 4241 | return MCValue{ .immediate = 1 }; | |
| 4242 | }, | |
| 4243 | else => unreachable, | |
| 4244 | } | |
| 4245 | } | |
| 4246 | ||
| 4247 | 4736 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4248 | 4737 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4249 | ||
| 4250 | try self.spillCompareFlagsIfOccupied(); | |
| 4251 | self.cpsr_flags_inst = inst; | |
| 4252 | ||
| 4253 | 4738 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4254 | const operand = try self.resolveInst(un_op); | |
| 4255 | const ty = self.air.typeOf(un_op); | |
| 4256 | break :result try self.isNull(ty, operand); | |
| 4739 | const operand_bind: ReadArg.Bind = .{ .inst = un_op }; | |
| 4740 | const operand_ty = self.air.typeOf(un_op); | |
| 4741 | ||
| 4742 | break :result try self.isNull(operand_bind, operand_ty); | |
| 4257 | 4743 | }; |
| 4258 | 4744 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4259 | 4745 | } |
| ... | ... | @@ -4263,16 +4749,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4263 | 4749 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4264 | 4750 | const operand_ptr = try self.resolveInst(un_op); |
| 4265 | 4751 | const ptr_ty = self.air.typeOf(un_op); |
| 4266 | const operand: MCValue = blk: { | |
| 4267 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | |
| 4268 | // The MCValue that holds the pointer can be re-used as the value. | |
| 4269 | break :blk operand_ptr; | |
| 4270 | } else { | |
| 4271 | break :blk try self.allocRegOrMem(inst, true); | |
| 4272 | } | |
| 4273 | }; | |
| 4752 | const elem_ty = ptr_ty.elemType(); | |
| 4753 | ||
| 4754 | const operand = try self.allocRegOrMem(elem_ty, true, null); | |
| 4274 | 4755 | try self.load(operand, operand_ptr, ptr_ty); |
| 4275 | break :result try self.isNull(ptr_ty.elemType(), operand); | |
| 4756 | ||
| 4757 | break :result try self.isNull(.{ .mcv = operand }, elem_ty); | |
| 4276 | 4758 | }; |
| 4277 | 4759 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4278 | 4760 | } |
| ... | ... | @@ -4280,9 +4762,10 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4280 | 4762 | fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4281 | 4763 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4282 | 4764 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4283 | const operand = try self.resolveInst(un_op); | |
| 4284 | const ty = self.air.typeOf(un_op); | |
| 4285 | break :result try self.isNonNull(ty, operand); | |
| 4765 | const operand_bind: ReadArg.Bind = .{ .inst = un_op }; | |
| 4766 | const operand_ty = self.air.typeOf(un_op); | |
| 4767 | ||
| 4768 | break :result try self.isNonNull(operand_bind, operand_ty); | |
| 4286 | 4769 | }; |
| 4287 | 4770 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4288 | 4771 | } |
| ... | ... | @@ -4292,26 +4775,57 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4292 | 4775 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4293 | 4776 | const operand_ptr = try self.resolveInst(un_op); |
| 4294 | 4777 | const ptr_ty = self.air.typeOf(un_op); |
| 4295 | const operand: MCValue = blk: { | |
| 4296 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | |
| 4297 | // The MCValue that holds the pointer can be re-used as the value. | |
| 4298 | break :blk operand_ptr; | |
| 4299 | } else { | |
| 4300 | break :blk try self.allocRegOrMem(inst, true); | |
| 4301 | } | |
| 4302 | }; | |
| 4778 | const elem_ty = ptr_ty.elemType(); | |
| 4779 | ||
| 4780 | const operand = try self.allocRegOrMem(elem_ty, true, null); | |
| 4303 | 4781 | try self.load(operand, operand_ptr, ptr_ty); |
| 4304 | break :result try self.isNonNull(ptr_ty.elemType(), operand); | |
| 4782 | ||
| 4783 | break :result try self.isNonNull(.{ .mcv = operand }, elem_ty); | |
| 4305 | 4784 | }; |
| 4306 | 4785 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4307 | 4786 | } |
| 4308 | 4787 | |
| 4788 | fn isErr( | |
| 4789 | self: *Self, | |
| 4790 | error_union_bind: ReadArg.Bind, | |
| 4791 | error_union_ty: Type, | |
| 4792 | ) !MCValue { | |
| 4793 | const error_type = error_union_ty.errorUnionSet(); | |
| 4794 | ||
| 4795 | if (error_type.errorSetIsEmpty()) { | |
| 4796 | return MCValue{ .immediate = 0 }; // always false | |
| 4797 | } | |
| 4798 | ||
| 4799 | const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null); | |
| 4800 | return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); | |
| 4801 | } | |
| 4802 | ||
| 4803 | fn isNonErr( | |
| 4804 | self: *Self, | |
| 4805 | error_union_bind: ReadArg.Bind, | |
| 4806 | error_union_ty: Type, | |
| 4807 | ) !MCValue { | |
| 4808 | const is_err_result = try self.isErr(error_union_bind, error_union_ty); | |
| 4809 | switch (is_err_result) { | |
| 4810 | .cpsr_flags => |cond| { | |
| 4811 | assert(cond == .hi); | |
| 4812 | return MCValue{ .cpsr_flags = cond.negate() }; | |
| 4813 | }, | |
| 4814 | .immediate => |imm| { | |
| 4815 | assert(imm == 0); | |
| 4816 | return MCValue{ .immediate = 1 }; | |
| 4817 | }, | |
| 4818 | else => unreachable, | |
| 4819 | } | |
| 4820 | } | |
| 4821 | ||
| 4309 | 4822 | fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 4310 | 4823 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4311 | 4824 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4312 | const operand = try self.resolveInst(un_op); | |
| 4313 | const ty = self.air.typeOf(un_op); | |
| 4314 | break :result try self.isErr(ty, operand); | |
| 4825 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; | |
| 4826 | const error_union_ty = self.air.typeOf(un_op); | |
| 4827 | ||
| 4828 | break :result try self.isErr(error_union_bind, error_union_ty); | |
| 4315 | 4829 | }; |
| 4316 | 4830 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4317 | 4831 | } |
| ... | ... | @@ -4321,16 +4835,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4321 | 4835 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4322 | 4836 | const operand_ptr = try self.resolveInst(un_op); |
| 4323 | 4837 | const ptr_ty = self.air.typeOf(un_op); |
| 4324 | const operand: MCValue = blk: { | |
| 4325 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | |
| 4326 | // The MCValue that holds the pointer can be re-used as the value. | |
| 4327 | break :blk operand_ptr; | |
| 4328 | } else { | |
| 4329 | break :blk try self.allocRegOrMem(inst, true); | |
| 4330 | } | |
| 4331 | }; | |
| 4838 | const elem_ty = ptr_ty.elemType(); | |
| 4839 | ||
| 4840 | const operand = try self.allocRegOrMem(elem_ty, true, null); | |
| 4332 | 4841 | try self.load(operand, operand_ptr, ptr_ty); |
| 4333 | break :result try self.isErr(ptr_ty.elemType(), operand); | |
| 4842 | ||
| 4843 | break :result try self.isErr(.{ .mcv = operand }, elem_ty); | |
| 4334 | 4844 | }; |
| 4335 | 4845 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4336 | 4846 | } |
| ... | ... | @@ -4338,9 +4848,10 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4338 | 4848 | fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 4339 | 4849 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4340 | 4850 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4341 | const operand = try self.resolveInst(un_op); | |
| 4342 | const ty = self.air.typeOf(un_op); | |
| 4343 | break :result try self.isNonErr(ty, operand); | |
| 4851 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; | |
| 4852 | const error_union_ty = self.air.typeOf(un_op); | |
| 4853 | ||
| 4854 | break :result try self.isNonErr(error_union_bind, error_union_ty); | |
| 4344 | 4855 | }; |
| 4345 | 4856 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4346 | 4857 | } |
| ... | ... | @@ -4350,16 +4861,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4350 | 4861 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4351 | 4862 | const operand_ptr = try self.resolveInst(un_op); |
| 4352 | 4863 | const ptr_ty = self.air.typeOf(un_op); |
| 4353 | const operand: MCValue = blk: { | |
| 4354 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | |
| 4355 | // The MCValue that holds the pointer can be re-used as the value. | |
| 4356 | break :blk operand_ptr; | |
| 4357 | } else { | |
| 4358 | break :blk try self.allocRegOrMem(inst, true); | |
| 4359 | } | |
| 4360 | }; | |
| 4864 | const elem_ty = ptr_ty.elemType(); | |
| 4865 | ||
| 4866 | const operand = try self.allocRegOrMem(elem_ty, true, null); | |
| 4361 | 4867 | try self.load(operand, operand_ptr, ptr_ty); |
| 4362 | break :result try self.isNonErr(ptr_ty.elemType(), operand); | |
| 4868 | ||
| 4869 | break :result try self.isNonErr(.{ .mcv = operand }, elem_ty); | |
| 4363 | 4870 | }; |
| 4364 | 4871 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4365 | 4872 | } |
| ... | ... | @@ -4456,14 +4963,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 4456 | 4963 | defer self.gpa.free(branch_into_prong_relocs); |
| 4457 | 4964 | |
| 4458 | 4965 | for (items) |item, idx| { |
| 4459 | const condition = try self.resolveInst(pl_op.operand); | |
| 4460 | const item_mcv = try self.resolveInst(item); | |
| 4461 | ||
| 4462 | const operands: BinOpOperands = .{ .mcv = .{ | |
| 4463 | .lhs = condition, | |
| 4464 | .rhs = item_mcv, | |
| 4465 | } }; | |
| 4466 | const cmp_result = try self.cmp(operands, condition_ty, .neq); | |
| 4966 | const cmp_result = try self.cmp(.{ .inst = pl_op.operand }, .{ .inst = item }, condition_ty, .neq); | |
| 4467 | 4967 | branch_into_prong_relocs[idx] = try self.condBr(cmp_result); |
| 4468 | 4968 | } |
| 4469 | 4969 | |
| ... | ... | @@ -4579,7 +5079,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 4579 | 5079 | .none, .dead, .unreach => unreachable, |
| 4580 | 5080 | .register, .stack_offset, .memory => operand_mcv, |
| 4581 | 5081 | .immediate, .stack_argument_offset, .cpsr_flags => blk: { |
| 4582 | const new_mcv = try self.allocRegOrMem(block, true); | |
| 5082 | const new_mcv = try self.allocRegOrMem(self.air.typeOfIndex(block), true, block); | |
| 4583 | 5083 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 4584 | 5084 | break :blk new_mcv; |
| 4585 | 5085 | }, |
| ... | ... | @@ -4832,9 +5332,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4832 | 5332 | .register_v_flag => .vs, |
| 4833 | 5333 | else => unreachable, |
| 4834 | 5334 | }, |
| 4835 | .data = .{ .rr_op = .{ | |
| 5335 | .data = .{ .r_op_mov = .{ | |
| 4836 | 5336 | .rd = cond_reg, |
| 4837 | .rn = .r0, | |
| 4838 | 5337 | .op = Instruction.Operand.fromU32(1).?, |
| 4839 | 5338 | } }, |
| 4840 | 5339 | }); |
| ... | ... | @@ -4935,9 +5434,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4935 | 5434 | // mov reg, 0 |
| 4936 | 5435 | _ = try self.addInst(.{ |
| 4937 | 5436 | .tag = .mov, |
| 4938 | .data = .{ .rr_op = .{ | |
| 5437 | .data = .{ .r_op_mov = .{ | |
| 4939 | 5438 | .rd = reg, |
| 4940 | .rn = .r0, | |
| 4941 | 5439 | .op = zero, |
| 4942 | 5440 | } }, |
| 4943 | 5441 | }); |
| ... | ... | @@ -4946,9 +5444,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4946 | 5444 | _ = try self.addInst(.{ |
| 4947 | 5445 | .tag = .mov, |
| 4948 | 5446 | .cond = condition, |
| 4949 | .data = .{ .rr_op = .{ | |
| 5447 | .data = .{ .r_op_mov = .{ | |
| 4950 | 5448 | .rd = reg, |
| 4951 | .rn = .r0, | |
| 4952 | 5449 | .op = one, |
| 4953 | 5450 | } }, |
| 4954 | 5451 | }); |
| ... | ... | @@ -4957,18 +5454,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4957 | 5454 | if (Instruction.Operand.fromU32(x)) |op| { |
| 4958 | 5455 | _ = try self.addInst(.{ |
| 4959 | 5456 | .tag = .mov, |
| 4960 | .data = .{ .rr_op = .{ | |
| 5457 | .data = .{ .r_op_mov = .{ | |
| 4961 | 5458 | .rd = reg, |
| 4962 | .rn = .r0, | |
| 4963 | 5459 | .op = op, |
| 4964 | 5460 | } }, |
| 4965 | 5461 | }); |
| 4966 | 5462 | } else if (Instruction.Operand.fromU32(~x)) |op| { |
| 4967 | 5463 | _ = try self.addInst(.{ |
| 4968 | 5464 | .tag = .mvn, |
| 4969 | .data = .{ .rr_op = .{ | |
| 5465 | .data = .{ .r_op_mov = .{ | |
| 4970 | 5466 | .rd = reg, |
| 4971 | .rn = .r0, | |
| 4972 | 5467 | .op = op, |
| 4973 | 5468 | } }, |
| 4974 | 5469 | }); |
| ... | ... | @@ -4984,9 +5479,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4984 | 5479 | } else { |
| 4985 | 5480 | _ = try self.addInst(.{ |
| 4986 | 5481 | .tag = .mov, |
| 4987 | .data = .{ .rr_op = .{ | |
| 5482 | .data = .{ .r_op_mov = .{ | |
| 4988 | 5483 | .rd = reg, |
| 4989 | .rn = .r0, | |
| 4990 | 5484 | .op = Instruction.Operand.imm(@truncate(u8, x), 0), |
| 4991 | 5485 | } }, |
| 4992 | 5486 | }); |
| ... | ... | @@ -5028,9 +5522,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5028 | 5522 | // orr reg, reg, #0xdd, 8 |
| 5029 | 5523 | _ = try self.addInst(.{ |
| 5030 | 5524 | .tag = .mov, |
| 5031 | .data = .{ .rr_op = .{ | |
| 5525 | .data = .{ .r_op_mov = .{ | |
| 5032 | 5526 | .rd = reg, |
| 5033 | .rn = .r0, | |
| 5034 | 5527 | .op = Instruction.Operand.imm(@truncate(u8, x), 0), |
| 5035 | 5528 | } }, |
| 5036 | 5529 | }); |
| ... | ... | @@ -5069,9 +5562,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5069 | 5562 | // mov reg, src_reg |
| 5070 | 5563 | _ = try self.addInst(.{ |
| 5071 | 5564 | .tag = .mov, |
| 5072 | .data = .{ .rr_op = .{ | |
| 5565 | .data = .{ .r_op_mov = .{ | |
| 5073 | 5566 | .rd = reg, |
| 5074 | .rn = .r0, | |
| 5075 | 5567 | .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none), |
| 5076 | 5568 | } }, |
| 5077 | 5569 | }); |
| ... | ... | @@ -5307,7 +5799,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 5307 | 5799 | const array_ty = ptr_ty.childType(); |
| 5308 | 5800 | const array_len = @intCast(u32, array_ty.arrayLen()); |
| 5309 | 5801 | |
| 5310 | const stack_offset = try self.allocMem(inst, 8, 8); | |
| 5802 | const stack_offset = try self.allocMem(8, 8, inst); | |
| 5311 | 5803 | try self.genSetStack(ptr_ty, stack_offset, ptr); |
| 5312 | 5804 | try self.genSetStack(Type.initTag(.usize), stack_offset - 4, .{ .immediate = array_len }); |
| 5313 | 5805 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -5461,15 +5953,24 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void { |
| 5461 | 5953 | const extra = self.air.extraData(Air.Try, pl_op.payload); |
| 5462 | 5954 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 5463 | 5955 | const result: MCValue = result: { |
| 5956 | const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand }; | |
| 5464 | 5957 | const error_union_ty = self.air.typeOf(pl_op.operand); |
| 5465 | const error_union = try self.resolveInst(pl_op.operand); | |
| 5466 | const is_err_result = try self.isErr(error_union_ty, error_union); | |
| 5958 | const error_union_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); | |
| 5959 | const error_union_align = error_union_ty.abiAlignment(self.target.*); | |
| 5960 | ||
| 5961 | // The error union will die in the body. However, we need the | |
| 5962 | // error union after the body in order to extract the payload | |
| 5963 | // of the error union, so we create a copy of it | |
| 5964 | const error_union_copy = try self.allocMem(error_union_size, error_union_align, null); | |
| 5965 | try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self)); | |
| 5966 | ||
| 5967 | const is_err_result = try self.isErr(error_union_bind, error_union_ty); | |
| 5467 | 5968 | const reloc = try self.condBr(is_err_result); |
| 5468 | 5969 | |
| 5469 | 5970 | try self.genBody(body); |
| 5470 | ||
| 5471 | 5971 | try self.performReloc(reloc); |
| 5472 | break :result try self.errUnionPayload(error_union, error_union_ty); | |
| 5972 | ||
| 5973 | break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null); | |
| 5473 | 5974 | }; |
| 5474 | 5975 | return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); |
| 5475 | 5976 | } |
src/arch/arm/Emit.zig+88-12| ... | ... | @@ -11,6 +11,7 @@ const link = @import("../../link.zig"); |
| 11 | 11 | const Module = @import("../../Module.zig"); |
| 12 | 12 | const Type = @import("../../type.zig").Type; |
| 13 | 13 | const ErrorMsg = Module.ErrorMsg; |
| 14 | const Target = std.Target; | |
| 14 | 15 | const assert = std.debug.assert; |
| 15 | 16 | const DW = std.dwarf; |
| 16 | 17 | const leb128 = std.leb; |
| ... | ... | @@ -93,6 +94,8 @@ pub fn emitMir( |
| 93 | 94 | .sub => try emit.mirDataProcessing(inst), |
| 94 | 95 | .subs => try emit.mirDataProcessing(inst), |
| 95 | 96 | |
| 97 | .sub_sp_scratch_r0 => try emit.mirSubStackPointer(inst), | |
| 98 | ||
| 96 | 99 | .asr => try emit.mirShift(inst), |
| 97 | 100 | .lsl => try emit.mirShift(inst), |
| 98 | 101 | .lsr => try emit.mirShift(inst), |
| ... | ... | @@ -190,6 +193,24 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { |
| 190 | 193 | .dbg_epilogue_begin, |
| 191 | 194 | .dbg_prologue_end, |
| 192 | 195 | => return 0, |
| 196 | ||
| 197 | .sub_sp_scratch_r0 => { | |
| 198 | const imm32 = emit.mir.instructions.items(.data)[inst].imm32; | |
| 199 | ||
| 200 | if (imm32 == 0) { | |
| 201 | return 0 * 4; | |
| 202 | } else if (Instruction.Operand.fromU32(imm32) != null) { | |
| 203 | // sub | |
| 204 | return 1 * 4; | |
| 205 | } else if (Target.arm.featureSetHas(emit.target.cpu.features, .has_v7)) { | |
| 206 | // movw; movt; sub | |
| 207 | return 3 * 4; | |
| 208 | } else { | |
| 209 | // mov; orr; orr; orr; sub | |
| 210 | return 5 * 4; | |
| 211 | } | |
| 212 | }, | |
| 213 | ||
| 193 | 214 | else => return 4, |
| 194 | 215 | } |
| 195 | 216 | } |
| ... | ... | @@ -385,20 +406,75 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { |
| 385 | 406 | fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 386 | 407 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 387 | 408 | const cond = emit.mir.instructions.items(.cond)[inst]; |
| 388 | const rr_op = emit.mir.instructions.items(.data)[inst].rr_op; | |
| 389 | 409 | |
| 390 | 410 | switch (tag) { |
| 391 | .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 392 | .adds => try emit.writeInstruction(Instruction.adds(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 393 | .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 394 | .cmp => try emit.writeInstruction(Instruction.cmp(cond, rr_op.rn, rr_op.op)), | |
| 395 | .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 396 | .mov => try emit.writeInstruction(Instruction.mov(cond, rr_op.rd, rr_op.op)), | |
| 397 | .mvn => try emit.writeInstruction(Instruction.mvn(cond, rr_op.rd, rr_op.op)), | |
| 398 | .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 399 | .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 400 | .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 401 | .subs => try emit.writeInstruction(Instruction.subs(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 411 | .add, | |
| 412 | .adds, | |
| 413 | .@"and", | |
| 414 | .eor, | |
| 415 | .orr, | |
| 416 | .rsb, | |
| 417 | .sub, | |
| 418 | .subs, | |
| 419 | => { | |
| 420 | const rr_op = emit.mir.instructions.items(.data)[inst].rr_op; | |
| 421 | switch (tag) { | |
| 422 | .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 423 | .adds => try emit.writeInstruction(Instruction.adds(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 424 | .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 425 | .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 426 | .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 427 | .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 428 | .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 429 | .subs => try emit.writeInstruction(Instruction.subs(cond, rr_op.rd, rr_op.rn, rr_op.op)), | |
| 430 | else => unreachable, | |
| 431 | } | |
| 432 | }, | |
| 433 | .cmp => { | |
| 434 | const r_op_cmp = emit.mir.instructions.items(.data)[inst].r_op_cmp; | |
| 435 | try emit.writeInstruction(Instruction.cmp(cond, r_op_cmp.rn, r_op_cmp.op)); | |
| 436 | }, | |
| 437 | .mov, | |
| 438 | .mvn, | |
| 439 | => { | |
| 440 | const r_op_mov = emit.mir.instructions.items(.data)[inst].r_op_mov; | |
| 441 | switch (tag) { | |
| 442 | .mov => try emit.writeInstruction(Instruction.mov(cond, r_op_mov.rd, r_op_mov.op)), | |
| 443 | .mvn => try emit.writeInstruction(Instruction.mvn(cond, r_op_mov.rd, r_op_mov.op)), | |
| 444 | else => unreachable, | |
| 445 | } | |
| 446 | }, | |
| 447 | else => unreachable, | |
| 448 | } | |
| 449 | } | |
| 450 | ||
| 451 | fn mirSubStackPointer(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 452 | const tag = emit.mir.instructions.items(.tag)[inst]; | |
| 453 | const cond = emit.mir.instructions.items(.cond)[inst]; | |
| 454 | const imm32 = emit.mir.instructions.items(.data)[inst].imm32; | |
| 455 | ||
| 456 | switch (tag) { | |
| 457 | .sub_sp_scratch_r0 => { | |
| 458 | if (imm32 == 0) return; | |
| 459 | ||
| 460 | const operand = Instruction.Operand.fromU32(imm32) orelse blk: { | |
| 461 | const scratch: Register = .r0; | |
| 462 | ||
| 463 | if (Target.arm.featureSetHas(emit.target.cpu.features, .has_v7)) { | |
| 464 | try emit.writeInstruction(Instruction.movw(cond, scratch, @truncate(u16, imm32))); | |
| 465 | try emit.writeInstruction(Instruction.movt(cond, scratch, @truncate(u16, imm32 >> 16))); | |
| 466 | } else { | |
| 467 | try emit.writeInstruction(Instruction.mov(cond, scratch, Instruction.Operand.imm(@truncate(u8, imm32), 0))); | |
| 468 | try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@truncate(u8, imm32 >> 8), 12))); | |
| 469 | try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@truncate(u8, imm32 >> 16), 8))); | |
| 470 | try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@truncate(u8, imm32 >> 24), 4))); | |
| 471 | } | |
| 472 | ||
| 473 | break :blk Instruction.Operand.reg(scratch, Instruction.Operand.Shift.none); | |
| 474 | }; | |
| 475 | ||
| 476 | try emit.writeInstruction(Instruction.sub(cond, .sp, .sp, operand)); | |
| 477 | }, | |
| 402 | 478 | else => unreachable, |
| 403 | 479 | } |
| 404 | 480 | } |
src/arch/arm/Mir.zig+23| ... | ... | @@ -111,6 +111,11 @@ pub const Inst = struct { |
| 111 | 111 | strh, |
| 112 | 112 | /// Subtract |
| 113 | 113 | sub, |
| 114 | /// Pseudo-instruction: Subtract 32-bit immediate from stack | |
| 115 | /// | |
| 116 | /// r0 can be used by Emit as a scratch register for loading | |
| 117 | /// the immediate | |
| 118 | sub_sp_scratch_r0, | |
| 114 | 119 | /// Subtract, update condition flags |
| 115 | 120 | subs, |
| 116 | 121 | /// Supervisor Call |
| ... | ... | @@ -144,6 +149,10 @@ pub const Inst = struct { |
| 144 | 149 | /// |
| 145 | 150 | /// Used by e.g. svc |
| 146 | 151 | imm24: u24, |
| 152 | /// A 32-bit immediate value. | |
| 153 | /// | |
| 154 | /// Used by e.g. sub_sp_scratch_r0 | |
| 155 | imm32: u32, | |
| 147 | 156 | /// Index into `extra`. Meaning of what can be found there is context-dependent. |
| 148 | 157 | /// |
| 149 | 158 | /// Used by e.g. load_memory |
| ... | ... | @@ -166,6 +175,20 @@ pub const Inst = struct { |
| 166 | 175 | rd: Register, |
| 167 | 176 | imm16: u16, |
| 168 | 177 | }, |
| 178 | /// A register and an operand | |
| 179 | /// | |
| 180 | /// Used by mov and mvn | |
| 181 | r_op_mov: struct { | |
| 182 | rd: Register, | |
| 183 | op: bits.Instruction.Operand, | |
| 184 | }, | |
| 185 | /// A register and an operand | |
| 186 | /// | |
| 187 | /// Used by cmp | |
| 188 | r_op_cmp: struct { | |
| 189 | rn: Register, | |
| 190 | op: bits.Instruction.Operand, | |
| 191 | }, | |
| 169 | 192 | /// Two registers and a shift amount |
| 170 | 193 | /// |
| 171 | 194 | /// Used by e.g. lsl |
src/arch/x86_64/CodeGen.zig+67-141| ... | ... | @@ -128,15 +128,11 @@ pub const MCValue = union(enum) { |
| 128 | 128 | /// The value is in memory at a hard-coded address. |
| 129 | 129 | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 130 | 130 | memory: u64, |
| 131 | /// The value is in memory referenced indirectly via a GOT entry index. | |
| 132 | /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. | |
| 133 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_GOT. | |
| 134 | got_load: u32, | |
| 135 | imports_load: u32, | |
| 136 | /// The value is in memory referenced directly via symbol index. | |
| 137 | /// If the type is a pointer, it means the pointer is referenced directly via symbol index. | |
| 138 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_SIGNED. | |
| 139 | direct_load: u32, | |
| 131 | /// The value is in memory but requires a linker relocation fixup: | |
| 132 | /// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc) | |
| 133 | /// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc) | |
| 134 | /// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc) | |
| 135 | linker_load: struct { @"type": enum { got, direct, import }, sym_index: u32 }, | |
| 140 | 136 | /// The value is one of the stack variables. |
| 141 | 137 | /// If the type is a pointer, it means the pointer address is in the stack at this offset. |
| 142 | 138 | stack_offset: i32, |
| ... | ... | @@ -150,9 +146,7 @@ pub const MCValue = union(enum) { |
| 150 | 146 | .memory, |
| 151 | 147 | .stack_offset, |
| 152 | 148 | .ptr_stack_offset, |
| 153 | .direct_load, | |
| 154 | .got_load, | |
| 155 | .imports_load, | |
| 149 | .linker_load, | |
| 156 | 150 | => true, |
| 157 | 151 | else => false, |
| 158 | 152 | }; |
| ... | ... | @@ -165,26 +159,6 @@ pub const MCValue = union(enum) { |
| 165 | 159 | }; |
| 166 | 160 | } |
| 167 | 161 | |
| 168 | fn isMutable(mcv: MCValue) bool { | |
| 169 | return switch (mcv) { | |
| 170 | .none => unreachable, | |
| 171 | .unreach => unreachable, | |
| 172 | .dead => unreachable, | |
| 173 | ||
| 174 | .immediate, | |
| 175 | .memory, | |
| 176 | .eflags, | |
| 177 | .ptr_stack_offset, | |
| 178 | .undef, | |
| 179 | .register_overflow, | |
| 180 | => false, | |
| 181 | ||
| 182 | .register, | |
| 183 | .stack_offset, | |
| 184 | => true, | |
| 185 | }; | |
| 186 | } | |
| 187 | ||
| 188 | 162 | fn isRegister(mcv: MCValue) bool { |
| 189 | 163 | return switch (mcv) { |
| 190 | 164 | .register => true, |
| ... | ... | @@ -2307,11 +2281,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2307 | 2281 | .data = .{ .imm = @bitCast(u32, -off) }, |
| 2308 | 2282 | }); |
| 2309 | 2283 | }, |
| 2310 | .memory, | |
| 2311 | .got_load, | |
| 2312 | .direct_load, | |
| 2313 | .imports_load, | |
| 2314 | => { | |
| 2284 | .memory, .linker_load => { | |
| 2315 | 2285 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); |
| 2316 | 2286 | }, |
| 2317 | 2287 | else => return self.fail("TODO implement array_elem_val when array is {}", .{array}), |
| ... | ... | @@ -2652,11 +2622,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2652 | 2622 | else => return self.fail("TODO implement loading from register into {}", .{dst_mcv}), |
| 2653 | 2623 | } |
| 2654 | 2624 | }, |
| 2655 | .memory, | |
| 2656 | .got_load, | |
| 2657 | .direct_load, | |
| 2658 | .imports_load, | |
| 2659 | => { | |
| 2625 | .memory, .linker_load => { | |
| 2660 | 2626 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 2661 | 2627 | try self.load(dst_mcv, .{ .register = reg }, ptr_ty); |
| 2662 | 2628 | }, |
| ... | ... | @@ -2691,10 +2657,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 2691 | 2657 | |
| 2692 | 2658 | fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue) InnerError!void { |
| 2693 | 2659 | switch (ptr) { |
| 2694 | .got_load, | |
| 2695 | .direct_load, | |
| 2696 | .imports_load, | |
| 2697 | => |sym_index| { | |
| 2660 | .linker_load => |load_struct| { | |
| 2698 | 2661 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); |
| 2699 | 2662 | const mod = self.bin_file.options.module.?; |
| 2700 | 2663 | const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); |
| ... | ... | @@ -2702,11 +2665,10 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2702 | 2665 | fn_owner_decl.link.macho.sym_index |
| 2703 | 2666 | else |
| 2704 | 2667 | fn_owner_decl.link.coff.sym_index; |
| 2705 | const flags: u2 = switch (ptr) { | |
| 2706 | .got_load => 0b00, | |
| 2707 | .direct_load => 0b01, | |
| 2708 | .imports_load => 0b10, | |
| 2709 | else => unreachable, | |
| 2668 | const flags: u2 = switch (load_struct.@"type") { | |
| 2669 | .got => 0b00, | |
| 2670 | .direct => 0b01, | |
| 2671 | .import => 0b10, | |
| 2710 | 2672 | }; |
| 2711 | 2673 | _ = try self.addInst(.{ |
| 2712 | 2674 | .tag = .lea_pic, |
| ... | ... | @@ -2717,7 +2679,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2717 | 2679 | .data = .{ |
| 2718 | 2680 | .relocation = .{ |
| 2719 | 2681 | .atom_index = atom_index, |
| 2720 | .sym_index = sym_index, | |
| 2682 | .sym_index = load_struct.sym_index, | |
| 2721 | 2683 | }, |
| 2722 | 2684 | }, |
| 2723 | 2685 | }); |
| ... | ... | @@ -2801,9 +2763,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2801 | 2763 | .register => |src_reg| { |
| 2802 | 2764 | try self.genInlineMemcpyRegisterRegister(value_ty, reg, src_reg, 0); |
| 2803 | 2765 | }, |
| 2804 | .got_load, | |
| 2805 | .direct_load, | |
| 2806 | .imports_load, | |
| 2766 | .linker_load, | |
| 2807 | 2767 | .memory, |
| 2808 | 2768 | .stack_offset, |
| 2809 | 2769 | => { |
| ... | ... | @@ -2822,11 +2782,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2822 | 2782 | }, |
| 2823 | 2783 | } |
| 2824 | 2784 | }, |
| 2825 | .got_load, | |
| 2826 | .direct_load, | |
| 2827 | .imports_load, | |
| 2828 | .memory, | |
| 2829 | => { | |
| 2785 | .linker_load, .memory => { | |
| 2830 | 2786 | const value_lock: ?RegisterLock = switch (value) { |
| 2831 | 2787 | .register => |reg| self.register_manager.lockReg(reg), |
| 2832 | 2788 | else => null, |
| ... | ... | @@ -2894,11 +2850,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2894 | 2850 | .register => { |
| 2895 | 2851 | return self.store(new_ptr, value, ptr_ty, value_ty); |
| 2896 | 2852 | }, |
| 2897 | .got_load, | |
| 2898 | .direct_load, | |
| 2899 | .imports_load, | |
| 2900 | .memory, | |
| 2901 | => { | |
| 2853 | .linker_load, .memory => { | |
| 2902 | 2854 | if (abi_size <= 8) { |
| 2903 | 2855 | const tmp_reg = try self.register_manager.allocReg(null, gp); |
| 2904 | 2856 | const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| ... | ... | @@ -3606,9 +3558,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3606 | 3558 | }); |
| 3607 | 3559 | }, |
| 3608 | 3560 | .memory, |
| 3609 | .got_load, | |
| 3610 | .direct_load, | |
| 3611 | .imports_load, | |
| 3561 | .linker_load, | |
| 3612 | 3562 | .eflags, |
| 3613 | 3563 | => { |
| 3614 | 3564 | assert(abi_size <= 8); |
| ... | ... | @@ -3694,10 +3644,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3694 | 3644 | => { |
| 3695 | 3645 | return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); |
| 3696 | 3646 | }, |
| 3697 | .got_load, | |
| 3698 | .direct_load, | |
| 3699 | .imports_load, | |
| 3700 | => { | |
| 3647 | .linker_load => { | |
| 3701 | 3648 | return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); |
| 3702 | 3649 | }, |
| 3703 | 3650 | .eflags => { |
| ... | ... | @@ -3708,10 +3655,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3708 | 3655 | .memory => { |
| 3709 | 3656 | return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{}); |
| 3710 | 3657 | }, |
| 3711 | .got_load, | |
| 3712 | .direct_load, | |
| 3713 | .imports_load, | |
| 3714 | => { | |
| 3658 | .linker_load => { | |
| 3715 | 3659 | return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{}); |
| 3716 | 3660 | }, |
| 3717 | 3661 | } |
| ... | ... | @@ -3779,10 +3723,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3779 | 3723 | .memory => { |
| 3780 | 3724 | return self.fail("TODO implement x86 multiply source memory", .{}); |
| 3781 | 3725 | }, |
| 3782 | .got_load, | |
| 3783 | .direct_load, | |
| 3784 | .imports_load, | |
| 3785 | => { | |
| 3726 | .linker_load => { | |
| 3786 | 3727 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3787 | 3728 | }, |
| 3788 | 3729 | .eflags => { |
| ... | ... | @@ -3826,10 +3767,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3826 | 3767 | .memory, .stack_offset => { |
| 3827 | 3768 | return self.fail("TODO implement x86 multiply source memory", .{}); |
| 3828 | 3769 | }, |
| 3829 | .got_load, | |
| 3830 | .direct_load, | |
| 3831 | .imports_load, | |
| 3832 | => { | |
| 3770 | .linker_load => { | |
| 3833 | 3771 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3834 | 3772 | }, |
| 3835 | 3773 | .eflags => { |
| ... | ... | @@ -3840,10 +3778,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3840 | 3778 | .memory => { |
| 3841 | 3779 | return self.fail("TODO implement x86 multiply destination memory", .{}); |
| 3842 | 3780 | }, |
| 3843 | .got_load, | |
| 3844 | .direct_load, | |
| 3845 | .imports_load, | |
| 3846 | => { | |
| 3781 | .linker_load => { | |
| 3847 | 3782 | return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{}); |
| 3848 | 3783 | }, |
| 3849 | 3784 | } |
| ... | ... | @@ -4006,9 +3941,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 4006 | 3941 | .unreach => unreachable, |
| 4007 | 3942 | .dead => unreachable, |
| 4008 | 3943 | .memory => unreachable, |
| 4009 | .got_load => unreachable, | |
| 4010 | .direct_load => unreachable, | |
| 4011 | .imports_load => unreachable, | |
| 3944 | .linker_load => unreachable, | |
| 4012 | 3945 | .eflags => unreachable, |
| 4013 | 3946 | .register_overflow => unreachable, |
| 4014 | 3947 | } |
| ... | ... | @@ -4066,7 +3999,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 4066 | 3999 | const func = func_payload.data; |
| 4067 | 4000 | const fn_owner_decl = mod.declPtr(func.owner_decl); |
| 4068 | 4001 | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 4069 | .got_load = fn_owner_decl.link.coff.sym_index, | |
| 4002 | .linker_load = .{ | |
| 4003 | .@"type" = .got, | |
| 4004 | .sym_index = fn_owner_decl.link.coff.sym_index, | |
| 4005 | }, | |
| 4070 | 4006 | }); |
| 4071 | 4007 | _ = try self.addInst(.{ |
| 4072 | 4008 | .tag = .call, |
| ... | ... | @@ -4087,7 +4023,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 4087 | 4023 | } |
| 4088 | 4024 | const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); |
| 4089 | 4025 | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 4090 | .imports_load = sym_index, | |
| 4026 | .linker_load = .{ | |
| 4027 | .@"type" = .import, | |
| 4028 | .sym_index = sym_index, | |
| 4029 | }, | |
| 4091 | 4030 | }); |
| 4092 | 4031 | _ = try self.addInst(.{ |
| 4093 | 4032 | .tag = .call, |
| ... | ... | @@ -4119,7 +4058,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 4119 | 4058 | const func = func_payload.data; |
| 4120 | 4059 | const fn_owner_decl = mod.declPtr(func.owner_decl); |
| 4121 | 4060 | const sym_index = fn_owner_decl.link.macho.sym_index; |
| 4122 | try self.genSetReg(Type.initTag(.usize), .rax, .{ .got_load = sym_index }); | |
| 4061 | try self.genSetReg(Type.initTag(.usize), .rax, .{ | |
| 4062 | .linker_load = .{ | |
| 4063 | .@"type" = .got, | |
| 4064 | .sym_index = sym_index, | |
| 4065 | }, | |
| 4066 | }); | |
| 4123 | 4067 | // callq *%rax |
| 4124 | 4068 | _ = try self.addInst(.{ |
| 4125 | 4069 | .tag = .call, |
| ... | ... | @@ -4505,11 +4449,7 @@ fn genVarDbgInfo( |
| 4505 | 4449 | leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; |
| 4506 | 4450 | dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); |
| 4507 | 4451 | }, |
| 4508 | .memory, | |
| 4509 | .got_load, | |
| 4510 | .direct_load, | |
| 4511 | .imports_load, | |
| 4512 | => { | |
| 4452 | .memory, .linker_load => { | |
| 4513 | 4453 | const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); |
| 4514 | 4454 | const is_ptr = switch (tag) { |
| 4515 | 4455 | .dbg_var_ptr => true, |
| ... | ... | @@ -4540,10 +4480,11 @@ fn genVarDbgInfo( |
| 4540 | 4480 | try dbg_info.append(DW.OP.deref); |
| 4541 | 4481 | } |
| 4542 | 4482 | switch (mcv) { |
| 4543 | .got_load, | |
| 4544 | .direct_load, | |
| 4545 | .imports_load, | |
| 4546 | => |index| try dw.addExprlocReloc(index, offset, is_ptr), | |
| 4483 | .linker_load => |load_struct| try dw.addExprlocReloc( | |
| 4484 | load_struct.sym_index, | |
| 4485 | offset, | |
| 4486 | is_ptr, | |
| 4487 | ), | |
| 4547 | 4488 | else => {}, |
| 4548 | 4489 | } |
| 4549 | 4490 | }, |
| ... | ... | @@ -5587,11 +5528,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5587 | 5528 | else => return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}), |
| 5588 | 5529 | } |
| 5589 | 5530 | }, |
| 5590 | .memory, | |
| 5591 | .direct_load, | |
| 5592 | .got_load, | |
| 5593 | .imports_load, | |
| 5594 | => { | |
| 5531 | .memory, .linker_load => { | |
| 5595 | 5532 | if (abi_size <= 8) { |
| 5596 | 5533 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 5597 | 5534 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| ... | ... | @@ -5835,11 +5772,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5835 | 5772 | }, |
| 5836 | 5773 | } |
| 5837 | 5774 | }, |
| 5838 | .memory, | |
| 5839 | .got_load, | |
| 5840 | .direct_load, | |
| 5841 | .imports_load, | |
| 5842 | => { | |
| 5775 | .memory, .linker_load => { | |
| 5843 | 5776 | if (abi_size <= 8) { |
| 5844 | 5777 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 5845 | 5778 | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }, opts); |
| ... | ... | @@ -5959,11 +5892,7 @@ fn genInlineMemcpy( |
| 5959 | 5892 | const tmp_reg = regs[4].to8(); |
| 5960 | 5893 | |
| 5961 | 5894 | switch (dst_ptr) { |
| 5962 | .memory, | |
| 5963 | .got_load, | |
| 5964 | .direct_load, | |
| 5965 | .imports_load, | |
| 5966 | => { | |
| 5895 | .memory, .linker_load => { | |
| 5967 | 5896 | try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); |
| 5968 | 5897 | }, |
| 5969 | 5898 | .ptr_stack_offset, .stack_offset => |off| { |
| ... | ... | @@ -5992,11 +5921,7 @@ fn genInlineMemcpy( |
| 5992 | 5921 | } |
| 5993 | 5922 | |
| 5994 | 5923 | switch (src_ptr) { |
| 5995 | .memory, | |
| 5996 | .got_load, | |
| 5997 | .direct_load, | |
| 5998 | .imports_load, | |
| 5999 | => { | |
| 5924 | .memory, .linker_load => { | |
| 6000 | 5925 | try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); |
| 6001 | 5926 | }, |
| 6002 | 5927 | .ptr_stack_offset, .stack_offset => |off| { |
| ... | ... | @@ -6120,11 +6045,7 @@ fn genInlineMemset( |
| 6120 | 6045 | const index_reg = regs[1].to64(); |
| 6121 | 6046 | |
| 6122 | 6047 | switch (dst_ptr) { |
| 6123 | .memory, | |
| 6124 | .got_load, | |
| 6125 | .direct_load, | |
| 6126 | .imports_load, | |
| 6127 | => { | |
| 6048 | .memory, .linker_load => { | |
| 6128 | 6049 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); |
| 6129 | 6050 | }, |
| 6130 | 6051 | .ptr_stack_offset, .stack_offset => |off| { |
| ... | ... | @@ -6356,10 +6277,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6356 | 6277 | .data = undefined, |
| 6357 | 6278 | }); |
| 6358 | 6279 | }, |
| 6359 | .direct_load, | |
| 6360 | .got_load, | |
| 6361 | .imports_load, | |
| 6362 | => { | |
| 6280 | .linker_load => { | |
| 6363 | 6281 | switch (ty.zigTypeTag()) { |
| 6364 | 6282 | .Float => { |
| 6365 | 6283 | const base_reg = try self.register_manager.allocReg(null, gp); |
| ... | ... | @@ -6753,11 +6671,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| 6753 | 6671 | // TODO Is this the only condition for pointer dereference for memcpy? |
| 6754 | 6672 | const src: MCValue = blk: { |
| 6755 | 6673 | switch (src_ptr) { |
| 6756 | .got_load, | |
| 6757 | .direct_load, | |
| 6758 | .imports_load, | |
| 6759 | .memory, | |
| 6760 | => { | |
| 6674 | .linker_load, .memory => { | |
| 6761 | 6675 | const reg = try self.register_manager.allocReg(null, gp); |
| 6762 | 6676 | try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); |
| 6763 | 6677 | _ = try self.addInst(.{ |
| ... | ... | @@ -6997,10 +6911,16 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 6997 | 6911 | return MCValue{ .memory = got_addr }; |
| 6998 | 6912 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6999 | 6913 | assert(decl.link.macho.sym_index != 0); |
| 7000 | return MCValue{ .got_load = decl.link.macho.sym_index }; | |
| 6914 | return MCValue{ .linker_load = .{ | |
| 6915 | .@"type" = .got, | |
| 6916 | .sym_index = decl.link.macho.sym_index, | |
| 6917 | } }; | |
| 7001 | 6918 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 7002 | 6919 | assert(decl.link.coff.sym_index != 0); |
| 7003 | return MCValue{ .got_load = decl.link.coff.sym_index }; | |
| 6920 | return MCValue{ .linker_load = .{ | |
| 6921 | .@"type" = .got, | |
| 6922 | .sym_index = decl.link.coff.sym_index, | |
| 6923 | } }; | |
| 7004 | 6924 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 7005 | 6925 | try p9.seeDecl(decl_index); |
| 7006 | 6926 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; |
| ... | ... | @@ -7019,9 +6939,15 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 7019 | 6939 | const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; |
| 7020 | 6940 | return MCValue{ .memory = vaddr }; |
| 7021 | 6941 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 7022 | return MCValue{ .direct_load = local_sym_index }; | |
| 6942 | return MCValue{ .linker_load = .{ | |
| 6943 | .@"type" = .direct, | |
| 6944 | .sym_index = local_sym_index, | |
| 6945 | } }; | |
| 7023 | 6946 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 7024 | return MCValue{ .direct_load = local_sym_index }; | |
| 6947 | return MCValue{ .linker_load = .{ | |
| 6948 | .@"type" = .direct, | |
| 6949 | .sym_index = local_sym_index, | |
| 6950 | } }; | |
| 7025 | 6951 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { |
| 7026 | 6952 | return self.fail("TODO lower unnamed const in Plan9", .{}); |
| 7027 | 6953 | } else { |
src/arch/x86_64/Emit.zig+11-8| ... | ... | @@ -1021,10 +1021,14 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1021 | 1021 | .@"type" = switch (ops.flags) { |
| 1022 | 1022 | 0b00 => .got, |
| 1023 | 1023 | 0b01 => .direct, |
| 1024 | 0b10 => .imports, | |
| 1024 | 0b10 => .import, | |
| 1025 | else => unreachable, | |
| 1026 | }, | |
| 1027 | .target = switch (ops.flags) { | |
| 1028 | 0b00, 0b01 => .{ .sym_index = relocation.sym_index, .file = null }, | |
| 1029 | 0b10 => coff_file.getGlobalByIndex(relocation.sym_index), | |
| 1025 | 1030 | else => unreachable, |
| 1026 | 1031 | }, |
| 1027 | .target = .{ .sym_index = relocation.sym_index, .file = null }, | |
| 1028 | 1032 | .offset = @intCast(u32, end_offset - 4), |
| 1029 | 1033 | .addend = 0, |
| 1030 | 1034 | .pcrel = true, |
| ... | ... | @@ -1142,12 +1146,10 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1142 | 1146 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 1143 | 1147 | // Add relocation to the decl. |
| 1144 | 1148 | const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?; |
| 1149 | const target = macho_file.getGlobalByIndex(relocation.sym_index); | |
| 1145 | 1150 | try atom.relocs.append(emit.bin_file.allocator, .{ |
| 1146 | 1151 | .offset = offset, |
| 1147 | .target = .{ | |
| 1148 | .sym_index = relocation.sym_index, | |
| 1149 | .file = null, | |
| 1150 | }, | |
| 1152 | .target = target, | |
| 1151 | 1153 | .addend = 0, |
| 1152 | 1154 | .subtractor = null, |
| 1153 | 1155 | .pcrel = true, |
| ... | ... | @@ -1157,16 +1159,17 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1157 | 1159 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { |
| 1158 | 1160 | // Add relocation to the decl. |
| 1159 | 1161 | const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?; |
| 1162 | const target = coff_file.getGlobalByIndex(relocation.sym_index); | |
| 1160 | 1163 | try atom.addRelocation(coff_file, .{ |
| 1161 | 1164 | .@"type" = .direct, |
| 1162 | .target = .{ .sym_index = relocation.sym_index, .file = null }, | |
| 1165 | .target = target, | |
| 1163 | 1166 | .offset = offset, |
| 1164 | 1167 | .addend = 0, |
| 1165 | 1168 | .pcrel = true, |
| 1166 | 1169 | .length = 2, |
| 1167 | 1170 | }); |
| 1168 | 1171 | } else { |
| 1169 | return emit.fail("TODO implement call_extern for linking backends different than MachO", .{}); | |
| 1172 | return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{}); | |
| 1170 | 1173 | } |
| 1171 | 1174 | } |
| 1172 | 1175 |
src/autodoc/render_source.zig+2-6| ... | ... | @@ -137,7 +137,7 @@ pub fn genHtml( |
| 137 | 137 | ); |
| 138 | 138 | |
| 139 | 139 | const source = try src.getSource(allocator); |
| 140 | try tokenizeAndPrintRaw(allocator, out, source.bytes); | |
| 140 | try tokenizeAndPrintRaw(out, source.bytes); | |
| 141 | 141 | try out.writeAll( |
| 142 | 142 | \\</body> |
| 143 | 143 | \\</html> |
| ... | ... | @@ -150,13 +150,9 @@ const end_line = "</span>\n"; |
| 150 | 150 | var line_counter: usize = 1; |
| 151 | 151 | |
| 152 | 152 | pub fn tokenizeAndPrintRaw( |
| 153 | allocator: Allocator, | |
| 154 | 153 | out: anytype, |
| 155 | raw_src: [:0]const u8, | |
| 154 | src: [:0]const u8, | |
| 156 | 155 | ) !void { |
| 157 | const src = try allocator.dupeZ(u8, raw_src); | |
| 158 | defer allocator.free(src); | |
| 159 | ||
| 160 | 156 | line_counter = 1; |
| 161 | 157 | |
| 162 | 158 | try out.print("<pre><code>" ++ start_line, .{line_counter}); |
src/glibc.zig+4-5| ... | ... | @@ -719,17 +719,16 @@ pub fn buildSharedObjects(comp: *Compilation) !void { |
| 719 | 719 | .lt => continue, |
| 720 | 720 | .gt => { |
| 721 | 721 | // TODO Expose via compile error mechanism instead of log. |
| 722 | log.err("invalid target glibc version: {}", .{target_version}); | |
| 722 | log.warn("invalid target glibc version: {}", .{target_version}); | |
| 723 | 723 | return error.InvalidTargetGLibCVersion; |
| 724 | 724 | }, |
| 725 | 725 | } |
| 726 | } else { | |
| 726 | } else blk: { | |
| 727 | 727 | const latest_index = metadata.all_versions.len - 1; |
| 728 | // TODO Expose via compile error mechanism instead of log. | |
| 729 | log.err("zig does not yet provide glibc version {}, the max provided version is {}", .{ | |
| 728 | log.warn("zig cannot build new glibc version {}; providing instead {}", .{ | |
| 730 | 729 | target_version, metadata.all_versions[latest_index], |
| 731 | 730 | }); |
| 732 | return error.InvalidTargetGLibCVersion; | |
| 731 | break :blk latest_index; | |
| 733 | 732 | }; |
| 734 | 733 | |
| 735 | 734 | { |
src/link/Coff.zig+61-18| ... | ... | @@ -127,7 +127,7 @@ pub const Reloc = struct { |
| 127 | 127 | @"type": enum { |
| 128 | 128 | got, |
| 129 | 129 | direct, |
| 130 | imports, | |
| 130 | import, | |
| 131 | 131 | }, |
| 132 | 132 | target: SymbolWithLoc, |
| 133 | 133 | offset: u32, |
| ... | ... | @@ -141,7 +141,7 @@ pub const Reloc = struct { |
| 141 | 141 | switch (self.@"type") { |
| 142 | 142 | .got => return coff_file.getGotAtomForSymbol(self.target), |
| 143 | 143 | .direct => return coff_file.getAtomForSymbol(self.target), |
| 144 | .imports => return coff_file.getImportAtomForSymbol(self.target), | |
| 144 | .import => return coff_file.getImportAtomForSymbol(self.target), | |
| 145 | 145 | } |
| 146 | 146 | } |
| 147 | 147 | }; |
| ... | ... | @@ -1423,23 +1423,22 @@ fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { |
| 1423 | 1423 | const sym = self.getSymbol(current); |
| 1424 | 1424 | const sym_name = self.getSymbolName(current); |
| 1425 | 1425 | |
| 1426 | const global_index = self.resolver.get(sym_name) orelse { | |
| 1427 | const name = try gpa.dupe(u8, sym_name); | |
| 1428 | const global_index = try self.allocateGlobal(); | |
| 1429 | self.globals.items[global_index] = current; | |
| 1430 | try self.resolver.putNoClobber(gpa, name, global_index); | |
| 1426 | const gop = try self.getOrPutGlobalPtr(sym_name); | |
| 1427 | if (!gop.found_existing) { | |
| 1428 | gop.value_ptr.* = current; | |
| 1431 | 1429 | if (sym.section_number == .UNDEFINED) { |
| 1432 | try self.unresolved.putNoClobber(gpa, global_index, false); | |
| 1430 | try self.unresolved.putNoClobber(gpa, self.getGlobalIndex(sym_name).?, false); | |
| 1433 | 1431 | } |
| 1434 | 1432 | return; |
| 1435 | }; | |
| 1433 | } | |
| 1436 | 1434 | |
| 1437 | 1435 | log.debug("TODO finish resolveGlobalSymbols implementation", .{}); |
| 1438 | 1436 | |
| 1439 | 1437 | if (sym.section_number == .UNDEFINED) return; |
| 1440 | 1438 | |
| 1441 | _ = self.unresolved.swapRemove(global_index); | |
| 1442 | self.globals.items[global_index] = current; | |
| 1439 | _ = self.unresolved.swapRemove(self.getGlobalIndex(sym_name).?); | |
| 1440 | ||
| 1441 | gop.value_ptr.* = current; | |
| 1443 | 1442 | } |
| 1444 | 1443 | |
| 1445 | 1444 | pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | ... | @@ -1544,25 +1543,26 @@ pub fn getDeclVAddr( |
| 1544 | 1543 | } |
| 1545 | 1544 | |
| 1546 | 1545 | pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 { |
| 1547 | if (self.resolver.get(name)) |global_index| { | |
| 1548 | return self.globals.items[global_index].sym_index; | |
| 1546 | const gop = try self.getOrPutGlobalPtr(name); | |
| 1547 | const global_index = self.getGlobalIndex(name).?; | |
| 1548 | ||
| 1549 | if (gop.found_existing) { | |
| 1550 | return global_index; | |
| 1549 | 1551 | } |
| 1550 | 1552 | |
| 1551 | const gpa = self.base.allocator; | |
| 1552 | 1553 | const sym_index = try self.allocateSymbol(); |
| 1553 | const global_index = try self.allocateGlobal(); | |
| 1554 | 1554 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; |
| 1555 | self.globals.items[global_index] = sym_loc; | |
| 1555 | gop.value_ptr.* = sym_loc; | |
| 1556 | 1556 | |
| 1557 | const gpa = self.base.allocator; | |
| 1557 | 1558 | const sym_name = try gpa.dupe(u8, name); |
| 1558 | 1559 | const sym = self.getSymbolPtr(sym_loc); |
| 1559 | 1560 | try self.setSymbolName(sym, sym_name); |
| 1560 | 1561 | sym.storage_class = .EXTERNAL; |
| 1561 | 1562 | |
| 1562 | try self.resolver.putNoClobber(gpa, sym_name, global_index); | |
| 1563 | 1563 | try self.unresolved.putNoClobber(gpa, global_index, true); |
| 1564 | 1564 | |
| 1565 | return sym_index; | |
| 1565 | return global_index; | |
| 1566 | 1566 | } |
| 1567 | 1567 | |
| 1568 | 1568 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -2061,6 +2061,49 @@ pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { |
| 2061 | 2061 | return self.strtab.get(offset).?; |
| 2062 | 2062 | } |
| 2063 | 2063 | |
| 2064 | /// Returns pointer to the global entry for `name` if one exists. | |
| 2065 | pub fn getGlobalPtr(self: *Coff, name: []const u8) ?*SymbolWithLoc { | |
| 2066 | const global_index = self.resolver.get(name) orelse return null; | |
| 2067 | return &self.globals.items[global_index]; | |
| 2068 | } | |
| 2069 | ||
| 2070 | /// Returns the global entry for `name` if one exists. | |
| 2071 | pub fn getGlobal(self: *const Coff, name: []const u8) ?SymbolWithLoc { | |
| 2072 | const global_index = self.resolver.get(name) orelse return null; | |
| 2073 | return self.globals.items[global_index]; | |
| 2074 | } | |
| 2075 | ||
| 2076 | /// Returns the index of the global entry for `name` if one exists. | |
| 2077 | pub fn getGlobalIndex(self: *const Coff, name: []const u8) ?u32 { | |
| 2078 | return self.resolver.get(name); | |
| 2079 | } | |
| 2080 | ||
| 2081 | /// Returns global entry at `index`. | |
| 2082 | pub fn getGlobalByIndex(self: *const Coff, index: u32) SymbolWithLoc { | |
| 2083 | assert(index < self.globals.items.len); | |
| 2084 | return self.globals.items[index]; | |
| 2085 | } | |
| 2086 | ||
| 2087 | const GetOrPutGlobalPtrResult = struct { | |
| 2088 | found_existing: bool, | |
| 2089 | value_ptr: *SymbolWithLoc, | |
| 2090 | }; | |
| 2091 | ||
| 2092 | /// Return pointer to the global entry for `name` if one exists. | |
| 2093 | /// Puts a new global entry for `name` if one doesn't exist, and | |
| 2094 | /// returns a pointer to it. | |
| 2095 | pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult { | |
| 2096 | if (self.getGlobalPtr(name)) |ptr| { | |
| 2097 | return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr }; | |
| 2098 | } | |
| 2099 | const gpa = self.base.allocator; | |
| 2100 | const global_index = try self.allocateGlobal(); | |
| 2101 | const global_name = try gpa.dupe(u8, name); | |
| 2102 | _ = try self.resolver.put(gpa, global_name, global_index); | |
| 2103 | const ptr = &self.globals.items[global_index]; | |
| 2104 | return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; | |
| 2105 | } | |
| 2106 | ||
| 2064 | 2107 | /// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor. |
| 2065 | 2108 | /// Returns null on failure. |
| 2066 | 2109 | pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
src/link/Coff/Atom.zig-10| ... | ... | @@ -111,13 +111,3 @@ pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void { |
| 111 | 111 | } |
| 112 | 112 | try gop.value_ptr.append(gpa, offset); |
| 113 | 113 | } |
| 114 | ||
| 115 | pub fn addBinding(self: *Atom, coff_file: *Coff, target: SymbolWithLoc) !void { | |
| 116 | const gpa = coff_file.base.allocator; | |
| 117 | log.debug(" (adding binding to target %{d} in %{d})", .{ target.sym_index, self.sym_index }); | |
| 118 | const gop = try coff_file.bindings.getOrPut(gpa, self); | |
| 119 | if (!gop.found_existing) { | |
| 120 | gop.value_ptr.* = .{}; | |
| 121 | } | |
| 122 | try gop.value_ptr.append(gpa, target); | |
| 123 | } |
src/link/MachO.zig+182-159| ... | ... | @@ -131,17 +131,12 @@ la_symbol_ptr_section_index: ?u8 = null, |
| 131 | 131 | data_section_index: ?u8 = null, |
| 132 | 132 | |
| 133 | 133 | locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 134 | globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, | |
| 135 | // FIXME Jakub | |
| 136 | // TODO storing index into globals might be dangerous if we delete a global | |
| 137 | // while not having everything resolved. Actually, perhaps `unresolved` | |
| 138 | // should not be stored at the global scope? Is this possible? | |
| 139 | // Otherwise, audit if this can be a problem. | |
| 140 | // An alternative, which I still need to investigate for perf reasons is to | |
| 141 | // store all global names in an adapted with context strtab. | |
| 134 | globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{}, | |
| 135 | resolver: std.StringHashMapUnmanaged(u32) = .{}, | |
| 142 | 136 | unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{}, |
| 143 | 137 | |
| 144 | 138 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 139 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 145 | 140 | |
| 146 | 141 | dyld_stub_binder_index: ?u32 = null, |
| 147 | 142 | dyld_private_atom: ?*Atom = null, |
| ... | ... | @@ -1917,7 +1912,7 @@ fn allocateSpecialSymbols(self: *MachO) !void { |
| 1917 | 1912 | "___dso_handle", |
| 1918 | 1913 | "__mh_execute_header", |
| 1919 | 1914 | }) |name| { |
| 1920 | const global = self.globals.get(name) orelse continue; | |
| 1915 | const global = self.getGlobal(name) orelse continue; | |
| 1921 | 1916 | if (global.file != null) continue; |
| 1922 | 1917 | const sym = self.getSymbolPtr(global); |
| 1923 | 1918 | const seg = self.segments.items[self.text_segment_cmd_index.?]; |
| ... | ... | @@ -2048,16 +2043,11 @@ fn writeAtomsIncremental(self: *MachO) !void { |
| 2048 | 2043 | |
| 2049 | 2044 | pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom { |
| 2050 | 2045 | const gpa = self.base.allocator; |
| 2051 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2052 | try self.locals.append(gpa, .{ | |
| 2053 | .n_strx = 0, | |
| 2054 | .n_type = macho.N_SECT, | |
| 2055 | .n_sect = 0, | |
| 2056 | .n_desc = 0, | |
| 2057 | .n_value = 0, | |
| 2058 | }); | |
| 2059 | ||
| 2046 | const sym_index = try self.allocateSymbol(); | |
| 2060 | 2047 | const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3); |
| 2048 | const sym = atom.getSymbolPtr(self); | |
| 2049 | sym.n_type = macho.N_SECT; | |
| 2050 | ||
| 2061 | 2051 | try atom.relocs.append(gpa, .{ |
| 2062 | 2052 | .offset = 0, |
| 2063 | 2053 | .target = target, |
| ... | ... | @@ -2074,7 +2064,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom { |
| 2074 | 2064 | |
| 2075 | 2065 | const target_sym = self.getSymbol(target); |
| 2076 | 2066 | if (target_sym.undf()) { |
| 2077 | const global = self.globals.get(self.getSymbolName(target)).?; | |
| 2067 | const global = self.getGlobal(self.getSymbolName(target)).?; | |
| 2078 | 2068 | try atom.bindings.append(gpa, .{ |
| 2079 | 2069 | .target = global, |
| 2080 | 2070 | .offset = 0, |
| ... | ... | @@ -2093,20 +2083,15 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom { |
| 2093 | 2083 | |
| 2094 | 2084 | pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom { |
| 2095 | 2085 | const gpa = self.base.allocator; |
| 2096 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2097 | try self.locals.append(gpa, .{ | |
| 2098 | .n_strx = 0, | |
| 2099 | .n_type = macho.N_SECT, | |
| 2100 | .n_sect = 0, | |
| 2101 | .n_desc = 0, | |
| 2102 | .n_value = 0, | |
| 2103 | }); | |
| 2104 | ||
| 2086 | const sym_index = try self.allocateSymbol(); | |
| 2105 | 2087 | const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3); |
| 2088 | const sym = atom.getSymbolPtr(self); | |
| 2089 | sym.n_type = macho.N_SECT; | |
| 2090 | ||
| 2106 | 2091 | const target_sym = self.getSymbol(target); |
| 2107 | 2092 | assert(target_sym.undf()); |
| 2108 | 2093 | |
| 2109 | const global = self.globals.get(self.getSymbolName(target)).?; | |
| 2094 | const global = self.getGlobal(self.getSymbolName(target)).?; | |
| 2110 | 2095 | try atom.bindings.append(gpa, .{ |
| 2111 | 2096 | .target = global, |
| 2112 | 2097 | .offset = 0, |
| ... | ... | @@ -2130,15 +2115,10 @@ fn createDyldPrivateAtom(self: *MachO) !void { |
| 2130 | 2115 | if (self.dyld_private_atom != null) return; |
| 2131 | 2116 | |
| 2132 | 2117 | const gpa = self.base.allocator; |
| 2133 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2134 | try self.locals.append(gpa, .{ | |
| 2135 | .n_strx = 0, | |
| 2136 | .n_type = macho.N_SECT, | |
| 2137 | .n_sect = 0, | |
| 2138 | .n_desc = 0, | |
| 2139 | .n_value = 0, | |
| 2140 | }); | |
| 2118 | const sym_index = try self.allocateSymbol(); | |
| 2141 | 2119 | const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3); |
| 2120 | const sym = atom.getSymbolPtr(self); | |
| 2121 | sym.n_type = macho.N_SECT; | |
| 2142 | 2122 | self.dyld_private_atom = atom; |
| 2143 | 2123 | |
| 2144 | 2124 | try self.allocateAtomCommon(atom, self.data_section_index.?); |
| ... | ... | @@ -2163,15 +2143,11 @@ fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 2163 | 2143 | .aarch64 => 2, |
| 2164 | 2144 | else => unreachable, |
| 2165 | 2145 | }; |
| 2166 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2167 | try self.locals.append(gpa, .{ | |
| 2168 | .n_strx = 0, | |
| 2169 | .n_type = macho.N_SECT, | |
| 2170 | .n_sect = 0, | |
| 2171 | .n_desc = 0, | |
| 2172 | .n_value = 0, | |
| 2173 | }); | |
| 2146 | const sym_index = try self.allocateSymbol(); | |
| 2174 | 2147 | const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment); |
| 2148 | const sym = atom.getSymbolPtr(self); | |
| 2149 | sym.n_type = macho.N_SECT; | |
| 2150 | ||
| 2175 | 2151 | const dyld_private_sym_index = self.dyld_private_atom.?.sym_index; |
| 2176 | 2152 | switch (arch) { |
| 2177 | 2153 | .x86_64 => { |
| ... | ... | @@ -2288,15 +2264,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 2288 | 2264 | .aarch64 => 2, |
| 2289 | 2265 | else => unreachable, |
| 2290 | 2266 | }; |
| 2291 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2292 | try self.locals.append(gpa, .{ | |
| 2293 | .n_strx = 0, | |
| 2294 | .n_type = macho.N_SECT, | |
| 2295 | .n_sect = 0, | |
| 2296 | .n_desc = 0, | |
| 2297 | .n_value = 0, | |
| 2298 | }); | |
| 2267 | const sym_index = try self.allocateSymbol(); | |
| 2299 | 2268 | const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment); |
| 2269 | const sym = atom.getSymbolPtr(self); | |
| 2270 | sym.n_type = macho.N_SECT; | |
| 2271 | ||
| 2300 | 2272 | try atom.relocs.ensureTotalCapacity(gpa, 1); |
| 2301 | 2273 | |
| 2302 | 2274 | switch (arch) { |
| ... | ... | @@ -2352,15 +2324,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 2352 | 2324 | |
| 2353 | 2325 | pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom { |
| 2354 | 2326 | const gpa = self.base.allocator; |
| 2355 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2356 | try self.locals.append(gpa, .{ | |
| 2357 | .n_strx = 0, | |
| 2358 | .n_type = macho.N_SECT, | |
| 2359 | .n_sect = 0, | |
| 2360 | .n_desc = 0, | |
| 2361 | .n_value = 0, | |
| 2362 | }); | |
| 2327 | const sym_index = try self.allocateSymbol(); | |
| 2363 | 2328 | const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3); |
| 2329 | const sym = atom.getSymbolPtr(self); | |
| 2330 | sym.n_type = macho.N_SECT; | |
| 2331 | ||
| 2364 | 2332 | try atom.relocs.append(gpa, .{ |
| 2365 | 2333 | .offset = 0, |
| 2366 | 2334 | .target = .{ .sym_index = stub_sym_index, .file = null }, |
| ... | ... | @@ -2376,7 +2344,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi |
| 2376 | 2344 | }); |
| 2377 | 2345 | try atom.rebases.append(gpa, 0); |
| 2378 | 2346 | |
| 2379 | const global = self.globals.get(self.getSymbolName(target)).?; | |
| 2347 | const global = self.getGlobal(self.getSymbolName(target)).?; | |
| 2380 | 2348 | try atom.lazy_bindings.append(gpa, .{ |
| 2381 | 2349 | .target = global, |
| 2382 | 2350 | .offset = 0, |
| ... | ... | @@ -2403,15 +2371,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 2403 | 2371 | .aarch64 => 3 * @sizeOf(u32), |
| 2404 | 2372 | else => unreachable, // unhandled architecture type |
| 2405 | 2373 | }; |
| 2406 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2407 | try self.locals.append(gpa, .{ | |
| 2408 | .n_strx = 0, | |
| 2409 | .n_type = macho.N_SECT, | |
| 2410 | .n_sect = 0, | |
| 2411 | .n_desc = 0, | |
| 2412 | .n_value = 0, | |
| 2413 | }); | |
| 2374 | const sym_index = try self.allocateSymbol(); | |
| 2414 | 2375 | const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment); |
| 2376 | const sym = atom.getSymbolPtr(self); | |
| 2377 | sym.n_type = macho.N_SECT; | |
| 2378 | ||
| 2415 | 2379 | switch (arch) { |
| 2416 | 2380 | .x86_64 => { |
| 2417 | 2381 | // jmp |
| ... | ... | @@ -2472,7 +2436,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 2472 | 2436 | fn createTentativeDefAtoms(self: *MachO) !void { |
| 2473 | 2437 | const gpa = self.base.allocator; |
| 2474 | 2438 | |
| 2475 | for (self.globals.values()) |global| { | |
| 2439 | for (self.globals.items) |global| { | |
| 2476 | 2440 | const sym = self.getSymbolPtr(global); |
| 2477 | 2441 | if (!sym.tentative()) continue; |
| 2478 | 2442 | |
| ... | ... | @@ -2516,51 +2480,44 @@ fn createTentativeDefAtoms(self: *MachO) !void { |
| 2516 | 2480 | |
| 2517 | 2481 | fn createMhExecuteHeaderSymbol(self: *MachO) !void { |
| 2518 | 2482 | if (self.base.options.output_mode != .Exe) return; |
| 2519 | if (self.globals.get("__mh_execute_header")) |global| { | |
| 2483 | if (self.getGlobal("__mh_execute_header")) |global| { | |
| 2520 | 2484 | const sym = self.getSymbol(global); |
| 2521 | 2485 | if (!sym.undf() and !(sym.pext() or sym.weakDef())) return; |
| 2522 | 2486 | } |
| 2523 | 2487 | |
| 2524 | 2488 | const gpa = self.base.allocator; |
| 2525 | const n_strx = try self.strtab.insert(gpa, "__mh_execute_header"); | |
| 2526 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2527 | try self.locals.append(gpa, .{ | |
| 2528 | .n_strx = n_strx, | |
| 2489 | const sym_index = try self.allocateSymbol(); | |
| 2490 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 2491 | const sym = self.getSymbolPtr(sym_loc); | |
| 2492 | sym.* = .{ | |
| 2493 | .n_strx = try self.strtab.insert(gpa, "__mh_execute_header"), | |
| 2529 | 2494 | .n_type = macho.N_SECT | macho.N_EXT, |
| 2530 | 2495 | .n_sect = 0, |
| 2531 | 2496 | .n_desc = macho.REFERENCED_DYNAMICALLY, |
| 2532 | 2497 | .n_value = 0, |
| 2533 | }); | |
| 2534 | ||
| 2535 | const name = try gpa.dupe(u8, "__mh_execute_header"); | |
| 2536 | const gop = try self.globals.getOrPut(gpa, name); | |
| 2537 | defer if (gop.found_existing) gpa.free(name); | |
| 2538 | gop.value_ptr.* = .{ | |
| 2539 | .sym_index = sym_index, | |
| 2540 | .file = null, | |
| 2541 | 2498 | }; |
| 2499 | ||
| 2500 | const gop = try self.getOrPutGlobalPtr("__mh_execute_header"); | |
| 2501 | gop.value_ptr.* = sym_loc; | |
| 2542 | 2502 | } |
| 2543 | 2503 | |
| 2544 | 2504 | fn createDsoHandleSymbol(self: *MachO) !void { |
| 2545 | const global = self.globals.getPtr("___dso_handle") orelse return; | |
| 2546 | const sym = self.getSymbolPtr(global.*); | |
| 2547 | if (!sym.undf()) return; | |
| 2505 | const global = self.getGlobalPtr("___dso_handle") orelse return; | |
| 2506 | if (!self.getSymbol(global.*).undf()) return; | |
| 2548 | 2507 | |
| 2549 | 2508 | const gpa = self.base.allocator; |
| 2550 | const n_strx = try self.strtab.insert(gpa, "___dso_handle"); | |
| 2551 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2552 | try self.locals.append(gpa, .{ | |
| 2553 | .n_strx = n_strx, | |
| 2509 | const sym_index = try self.allocateSymbol(); | |
| 2510 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 2511 | const sym = self.getSymbolPtr(sym_loc); | |
| 2512 | sym.* = .{ | |
| 2513 | .n_strx = try self.strtab.insert(gpa, "___dso_handle"), | |
| 2554 | 2514 | .n_type = macho.N_SECT | macho.N_EXT, |
| 2555 | 2515 | .n_sect = 0, |
| 2556 | 2516 | .n_desc = macho.N_WEAK_DEF, |
| 2557 | 2517 | .n_value = 0, |
| 2558 | }); | |
| 2559 | global.* = .{ | |
| 2560 | .sym_index = sym_index, | |
| 2561 | .file = null, | |
| 2562 | 2518 | }; |
| 2563 | _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex("___dso_handle").?)); | |
| 2519 | global.* = sym_loc; | |
| 2520 | _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?); | |
| 2564 | 2521 | } |
| 2565 | 2522 | |
| 2566 | 2523 | fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { |
| ... | ... | @@ -2568,19 +2525,14 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { |
| 2568 | 2525 | const sym = self.getSymbol(current); |
| 2569 | 2526 | const sym_name = self.getSymbolName(current); |
| 2570 | 2527 | |
| 2571 | const name = try gpa.dupe(u8, sym_name); | |
| 2572 | const global_index = @intCast(u32, self.globals.values().len); | |
| 2573 | const gop = try self.globals.getOrPut(gpa, name); | |
| 2574 | defer if (gop.found_existing) gpa.free(name); | |
| 2575 | ||
| 2528 | const gop = try self.getOrPutGlobalPtr(sym_name); | |
| 2576 | 2529 | if (!gop.found_existing) { |
| 2577 | 2530 | gop.value_ptr.* = current; |
| 2578 | 2531 | if (sym.undf() and !sym.tentative()) { |
| 2579 | try self.unresolved.putNoClobber(gpa, global_index, false); | |
| 2532 | try self.unresolved.putNoClobber(gpa, self.getGlobalIndex(sym_name).?, false); | |
| 2580 | 2533 | } |
| 2581 | 2534 | return; |
| 2582 | 2535 | } |
| 2583 | ||
| 2584 | 2536 | const global = gop.value_ptr.*; |
| 2585 | 2537 | const global_sym = self.getSymbol(global); |
| 2586 | 2538 | |
| ... | ... | @@ -2619,7 +2571,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { |
| 2619 | 2571 | } |
| 2620 | 2572 | if (sym.undf() and !sym.tentative()) return; |
| 2621 | 2573 | |
| 2622 | _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex(name).?)); | |
| 2574 | _ = self.unresolved.swapRemove(self.getGlobalIndex(sym_name).?); | |
| 2623 | 2575 | |
| 2624 | 2576 | gop.value_ptr.* = current; |
| 2625 | 2577 | } |
| ... | ... | @@ -2664,7 +2616,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void { |
| 2664 | 2616 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id }; |
| 2665 | 2617 | self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) { |
| 2666 | 2618 | error.MultipleSymbolDefinitions => { |
| 2667 | const global = self.globals.get(sym_name).?; | |
| 2619 | const global = self.getGlobal(sym_name).?; | |
| 2668 | 2620 | log.err("symbol '{s}' defined multiple times", .{sym_name}); |
| 2669 | 2621 | if (global.file) |file| { |
| 2670 | 2622 | log.err(" first definition in '{s}'", .{self.objects.items[file].name}); |
| ... | ... | @@ -2684,7 +2636,8 @@ fn resolveSymbolsInArchives(self: *MachO) !void { |
| 2684 | 2636 | const cpu_arch = self.base.options.target.cpu.arch; |
| 2685 | 2637 | var next_sym: usize = 0; |
| 2686 | 2638 | loop: while (next_sym < self.unresolved.count()) { |
| 2687 | const global = self.globals.values()[self.unresolved.keys()[next_sym]]; | |
| 2639 | const global_index = self.unresolved.keys()[next_sym]; | |
| 2640 | const global = self.globals.items[global_index]; | |
| 2688 | 2641 | const sym_name = self.getSymbolName(global); |
| 2689 | 2642 | |
| 2690 | 2643 | for (self.archives.items) |archive| { |
| ... | ... | @@ -2710,10 +2663,11 @@ fn resolveSymbolsInArchives(self: *MachO) !void { |
| 2710 | 2663 | fn resolveSymbolsInDylibs(self: *MachO) !void { |
| 2711 | 2664 | if (self.dylibs.items.len == 0) return; |
| 2712 | 2665 | |
| 2666 | const gpa = self.base.allocator; | |
| 2713 | 2667 | var next_sym: usize = 0; |
| 2714 | 2668 | loop: while (next_sym < self.unresolved.count()) { |
| 2715 | 2669 | const global_index = self.unresolved.keys()[next_sym]; |
| 2716 | const global = self.globals.values()[global_index]; | |
| 2670 | const global = self.globals.items[global_index]; | |
| 2717 | 2671 | const sym = self.getSymbolPtr(global); |
| 2718 | 2672 | const sym_name = self.getSymbolName(global); |
| 2719 | 2673 | |
| ... | ... | @@ -2722,7 +2676,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void { |
| 2722 | 2676 | |
| 2723 | 2677 | const dylib_id = @intCast(u16, id); |
| 2724 | 2678 | if (!self.referenced_dylibs.contains(dylib_id)) { |
| 2725 | try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {}); | |
| 2679 | try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {}); | |
| 2726 | 2680 | } |
| 2727 | 2681 | |
| 2728 | 2682 | const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable; |
| ... | ... | @@ -2760,7 +2714,7 @@ fn resolveSymbolsAtLoading(self: *MachO) !void { |
| 2760 | 2714 | var next_sym: usize = 0; |
| 2761 | 2715 | while (next_sym < self.unresolved.count()) { |
| 2762 | 2716 | const global_index = self.unresolved.keys()[next_sym]; |
| 2763 | const global = self.globals.values()[global_index]; | |
| 2717 | const global = self.globals.items[global_index]; | |
| 2764 | 2718 | const sym = self.getSymbolPtr(global); |
| 2765 | 2719 | const sym_name = self.getSymbolName(global); |
| 2766 | 2720 | |
| ... | ... | @@ -2800,26 +2754,27 @@ fn resolveDyldStubBinder(self: *MachO) !void { |
| 2800 | 2754 | if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports |
| 2801 | 2755 | |
| 2802 | 2756 | const gpa = self.base.allocator; |
| 2803 | const n_strx = try self.strtab.insert(gpa, "dyld_stub_binder"); | |
| 2804 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 2805 | try self.locals.append(gpa, .{ | |
| 2806 | .n_strx = n_strx, | |
| 2757 | const sym_index = try self.allocateSymbol(); | |
| 2758 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 2759 | const sym = self.getSymbolPtr(sym_loc); | |
| 2760 | const sym_name = "dyld_stub_binder"; | |
| 2761 | sym.* = .{ | |
| 2762 | .n_strx = try self.strtab.insert(gpa, sym_name), | |
| 2807 | 2763 | .n_type = macho.N_UNDF, |
| 2808 | 2764 | .n_sect = 0, |
| 2809 | 2765 | .n_desc = 0, |
| 2810 | 2766 | .n_value = 0, |
| 2811 | }); | |
| 2812 | const sym_name = try gpa.dupe(u8, "dyld_stub_binder"); | |
| 2813 | const global = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 2814 | try self.globals.putNoClobber(gpa, sym_name, global); | |
| 2815 | const sym = &self.locals.items[sym_index]; | |
| 2767 | }; | |
| 2768 | const gop = try self.getOrPutGlobalPtr(sym_name); | |
| 2769 | gop.value_ptr.* = sym_loc; | |
| 2770 | const global = gop.value_ptr.*; | |
| 2816 | 2771 | |
| 2817 | 2772 | for (self.dylibs.items) |dylib, id| { |
| 2818 | 2773 | if (!dylib.symbols.contains(sym_name)) continue; |
| 2819 | 2774 | |
| 2820 | 2775 | const dylib_id = @intCast(u16, id); |
| 2821 | 2776 | if (!self.referenced_dylibs.contains(dylib_id)) { |
| 2822 | try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {}); | |
| 2777 | try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {}); | |
| 2823 | 2778 | } |
| 2824 | 2779 | |
| 2825 | 2780 | const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable; |
| ... | ... | @@ -3050,14 +3005,20 @@ pub fn deinit(self: *MachO) void { |
| 3050 | 3005 | self.stubs_free_list.deinit(gpa); |
| 3051 | 3006 | self.stubs_table.deinit(gpa); |
| 3052 | 3007 | self.strtab.deinit(gpa); |
| 3008 | ||
| 3053 | 3009 | self.locals.deinit(gpa); |
| 3010 | self.globals.deinit(gpa); | |
| 3054 | 3011 | self.locals_free_list.deinit(gpa); |
| 3012 | self.globals_free_list.deinit(gpa); | |
| 3055 | 3013 | self.unresolved.deinit(gpa); |
| 3056 | 3014 | |
| 3057 | for (self.globals.keys()) |key| { | |
| 3058 | gpa.free(key); | |
| 3015 | { | |
| 3016 | var it = self.resolver.keyIterator(); | |
| 3017 | while (it.next()) |key_ptr| { | |
| 3018 | gpa.free(key_ptr.*); | |
| 3019 | } | |
| 3020 | self.resolver.deinit(gpa); | |
| 3059 | 3021 | } |
| 3060 | self.globals.deinit(gpa); | |
| 3061 | 3022 | |
| 3062 | 3023 | for (self.objects.items) |*object| { |
| 3063 | 3024 | object.deinit(gpa); |
| ... | ... | @@ -3211,6 +3172,29 @@ fn allocateSymbol(self: *MachO) !u32 { |
| 3211 | 3172 | return index; |
| 3212 | 3173 | } |
| 3213 | 3174 | |
| 3175 | fn allocateGlobal(self: *MachO) !u32 { | |
| 3176 | try self.globals.ensureUnusedCapacity(self.base.allocator, 1); | |
| 3177 | ||
| 3178 | const index = blk: { | |
| 3179 | if (self.globals_free_list.popOrNull()) |index| { | |
| 3180 | log.debug(" (reusing global index {d})", .{index}); | |
| 3181 | break :blk index; | |
| 3182 | } else { | |
| 3183 | log.debug(" (allocating symbol index {d})", .{self.globals.items.len}); | |
| 3184 | const index = @intCast(u32, self.globals.items.len); | |
| 3185 | _ = self.globals.addOneAssumeCapacity(); | |
| 3186 | break :blk index; | |
| 3187 | } | |
| 3188 | }; | |
| 3189 | ||
| 3190 | self.globals.items[index] = .{ | |
| 3191 | .sym_index = 0, | |
| 3192 | .file = null, | |
| 3193 | }; | |
| 3194 | ||
| 3195 | return index; | |
| 3196 | } | |
| 3197 | ||
| 3214 | 3198 | pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 { |
| 3215 | 3199 | const gpa = self.base.allocator; |
| 3216 | 3200 | try self.got_entries.ensureUnusedCapacity(gpa, 1); |
| ... | ... | @@ -3832,7 +3816,7 @@ pub fn updateDeclExports( |
| 3832 | 3816 | |
| 3833 | 3817 | self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) { |
| 3834 | 3818 | error.MultipleSymbolDefinitions => { |
| 3835 | const global = self.globals.get(exp_name).?; | |
| 3819 | const global = self.getGlobal(exp_name).?; | |
| 3836 | 3820 | if (sym_loc.sym_index != global.sym_index and global.file != null) { |
| 3837 | 3821 | _ = try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create( |
| 3838 | 3822 | gpa, |
| ... | ... | @@ -3869,11 +3853,13 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 3869 | 3853 | }; |
| 3870 | 3854 | self.locals_free_list.append(gpa, sym_index) catch {}; |
| 3871 | 3855 | |
| 3872 | if (self.globals.get(sym_name)) |global| blk: { | |
| 3873 | if (global.sym_index != sym_index) break :blk; | |
| 3874 | if (global.file != null) break :blk; | |
| 3875 | const kv = self.globals.fetchSwapRemove(sym_name); | |
| 3876 | gpa.free(kv.?.key); | |
| 3856 | if (self.resolver.fetchRemove(sym_name)) |entry| { | |
| 3857 | defer gpa.free(entry.key); | |
| 3858 | self.globals_free_list.append(gpa, entry.value) catch {}; | |
| 3859 | self.globals.items[entry.value] = .{ | |
| 3860 | .sym_index = 0, | |
| 3861 | .file = null, | |
| 3862 | }; | |
| 3877 | 3863 | } |
| 3878 | 3864 | } |
| 3879 | 3865 | |
| ... | ... | @@ -4864,32 +4850,26 @@ pub fn addAtomToSection(self: *MachO, atom: *Atom, sect_id: u8) !void { |
| 4864 | 4850 | |
| 4865 | 4851 | pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 { |
| 4866 | 4852 | const gpa = self.base.allocator; |
| 4853 | ||
| 4867 | 4854 | const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); |
| 4868 | const global_index = @intCast(u32, self.globals.values().len); | |
| 4869 | const gop = try self.globals.getOrPut(gpa, sym_name); | |
| 4870 | defer if (gop.found_existing) gpa.free(sym_name); | |
| 4855 | defer gpa.free(sym_name); | |
| 4856 | const gop = try self.getOrPutGlobalPtr(sym_name); | |
| 4857 | const global_index = self.getGlobalIndex(sym_name).?; | |
| 4871 | 4858 | |
| 4872 | 4859 | if (gop.found_existing) { |
| 4873 | // TODO audit this: can we ever reference anything from outside the Zig module? | |
| 4874 | assert(gop.value_ptr.file == null); | |
| 4875 | return gop.value_ptr.sym_index; | |
| 4860 | return global_index; | |
| 4876 | 4861 | } |
| 4877 | 4862 | |
| 4878 | const sym_index = @intCast(u32, self.locals.items.len); | |
| 4879 | try self.locals.append(gpa, .{ | |
| 4880 | .n_strx = try self.strtab.insert(gpa, sym_name), | |
| 4881 | .n_type = macho.N_UNDF, | |
| 4882 | .n_sect = 0, | |
| 4883 | .n_desc = 0, | |
| 4884 | .n_value = 0, | |
| 4885 | }); | |
| 4886 | gop.value_ptr.* = .{ | |
| 4887 | .sym_index = sym_index, | |
| 4888 | .file = null, | |
| 4889 | }; | |
| 4863 | const sym_index = try self.allocateSymbol(); | |
| 4864 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 4865 | gop.value_ptr.* = sym_loc; | |
| 4866 | ||
| 4867 | const sym = self.getSymbolPtr(sym_loc); | |
| 4868 | sym.n_strx = try self.strtab.insert(gpa, sym_name); | |
| 4869 | ||
| 4890 | 4870 | try self.unresolved.putNoClobber(gpa, global_index, true); |
| 4891 | 4871 | |
| 4892 | return sym_index; | |
| 4872 | return global_index; | |
| 4893 | 4873 | } |
| 4894 | 4874 | |
| 4895 | 4875 | fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } { |
| ... | ... | @@ -5055,7 +5035,7 @@ fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void { |
| 5055 | 5035 | if (self.base.options.output_mode == .Exe) { |
| 5056 | 5036 | for (&[_]SymbolWithLoc{ |
| 5057 | 5037 | try self.getEntryPoint(), |
| 5058 | self.globals.get("__mh_execute_header").?, | |
| 5038 | self.getGlobal("__mh_execute_header").?, | |
| 5059 | 5039 | }) |global| { |
| 5060 | 5040 | const sym = self.getSymbol(global); |
| 5061 | 5041 | const sym_name = self.getSymbolName(global); |
| ... | ... | @@ -5068,7 +5048,7 @@ fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void { |
| 5068 | 5048 | } |
| 5069 | 5049 | } else { |
| 5070 | 5050 | assert(self.base.options.output_mode == .Lib); |
| 5071 | for (self.globals.values()) |global| { | |
| 5051 | for (self.globals.items) |global| { | |
| 5072 | 5052 | const sym = self.getSymbol(global); |
| 5073 | 5053 | |
| 5074 | 5054 | if (sym.undf()) continue; |
| ... | ... | @@ -5271,9 +5251,9 @@ fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void { |
| 5271 | 5251 | // We need to sort by address first |
| 5272 | 5252 | var addresses = std.ArrayList(u64).init(gpa); |
| 5273 | 5253 | defer addresses.deinit(); |
| 5274 | try addresses.ensureTotalCapacityPrecise(self.globals.count()); | |
| 5254 | try addresses.ensureTotalCapacityPrecise(self.globals.items.len); | |
| 5275 | 5255 | |
| 5276 | for (self.globals.values()) |global| { | |
| 5256 | for (self.globals.items) |global| { | |
| 5277 | 5257 | const sym = self.getSymbol(global); |
| 5278 | 5258 | if (sym.undf()) continue; |
| 5279 | 5259 | if (sym.n_desc == N_DESC_GCED) continue; |
| ... | ... | @@ -5453,7 +5433,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx { |
| 5453 | 5433 | if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip |
| 5454 | 5434 | const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null }; |
| 5455 | 5435 | if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip |
| 5456 | if (self.globals.contains(self.getSymbolName(sym_loc))) continue; // global symbol is either an export or import, skip | |
| 5436 | if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip | |
| 5457 | 5437 | try locals.append(sym); |
| 5458 | 5438 | } |
| 5459 | 5439 | |
| ... | ... | @@ -5463,7 +5443,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx { |
| 5463 | 5443 | if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip |
| 5464 | 5444 | const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = @intCast(u32, object_id) }; |
| 5465 | 5445 | if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip |
| 5466 | if (self.globals.contains(self.getSymbolName(sym_loc))) continue; // global symbol is either an export or import, skip | |
| 5446 | if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip | |
| 5467 | 5447 | var out_sym = sym; |
| 5468 | 5448 | out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc)); |
| 5469 | 5449 | try locals.append(out_sym); |
| ... | ... | @@ -5477,7 +5457,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx { |
| 5477 | 5457 | var exports = std.ArrayList(macho.nlist_64).init(gpa); |
| 5478 | 5458 | defer exports.deinit(); |
| 5479 | 5459 | |
| 5480 | for (self.globals.values()) |global| { | |
| 5460 | for (self.globals.items) |global| { | |
| 5481 | 5461 | const sym = self.getSymbol(global); |
| 5482 | 5462 | if (sym.undf()) continue; // import, skip |
| 5483 | 5463 | if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip |
| ... | ... | @@ -5491,7 +5471,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx { |
| 5491 | 5471 | |
| 5492 | 5472 | var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa); |
| 5493 | 5473 | |
| 5494 | for (self.globals.values()) |global| { | |
| 5474 | for (self.globals.items) |global| { | |
| 5495 | 5475 | const sym = self.getSymbol(global); |
| 5496 | 5476 | if (sym.n_strx == 0) continue; // no name, skip |
| 5497 | 5477 | if (!sym.undf()) continue; // not an import, skip |
| ... | ... | @@ -5798,6 +5778,49 @@ pub fn getSymbolName(self: *MachO, sym_with_loc: SymbolWithLoc) []const u8 { |
| 5798 | 5778 | } |
| 5799 | 5779 | } |
| 5800 | 5780 | |
| 5781 | /// Returns pointer to the global entry for `name` if one exists. | |
| 5782 | pub fn getGlobalPtr(self: *MachO, name: []const u8) ?*SymbolWithLoc { | |
| 5783 | const global_index = self.resolver.get(name) orelse return null; | |
| 5784 | return &self.globals.items[global_index]; | |
| 5785 | } | |
| 5786 | ||
| 5787 | /// Returns the global entry for `name` if one exists. | |
| 5788 | pub fn getGlobal(self: *const MachO, name: []const u8) ?SymbolWithLoc { | |
| 5789 | const global_index = self.resolver.get(name) orelse return null; | |
| 5790 | return self.globals.items[global_index]; | |
| 5791 | } | |
| 5792 | ||
| 5793 | /// Returns the index of the global entry for `name` if one exists. | |
| 5794 | pub fn getGlobalIndex(self: *const MachO, name: []const u8) ?u32 { | |
| 5795 | return self.resolver.get(name); | |
| 5796 | } | |
| 5797 | ||
| 5798 | /// Returns global entry at `index`. | |
| 5799 | pub fn getGlobalByIndex(self: *const MachO, index: u32) SymbolWithLoc { | |
| 5800 | assert(index < self.globals.items.len); | |
| 5801 | return self.globals.items[index]; | |
| 5802 | } | |
| 5803 | ||
| 5804 | const GetOrPutGlobalPtrResult = struct { | |
| 5805 | found_existing: bool, | |
| 5806 | value_ptr: *SymbolWithLoc, | |
| 5807 | }; | |
| 5808 | ||
| 5809 | /// Return pointer to the global entry for `name` if one exists. | |
| 5810 | /// Puts a new global entry for `name` if one doesn't exist, and | |
| 5811 | /// returns a pointer to it. | |
| 5812 | pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResult { | |
| 5813 | if (self.getGlobalPtr(name)) |ptr| { | |
| 5814 | return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr }; | |
| 5815 | } | |
| 5816 | const gpa = self.base.allocator; | |
| 5817 | const global_index = try self.allocateGlobal(); | |
| 5818 | const global_name = try gpa.dupe(u8, name); | |
| 5819 | _ = try self.resolver.put(gpa, global_name, global_index); | |
| 5820 | const ptr = &self.globals.items[global_index]; | |
| 5821 | return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; | |
| 5822 | } | |
| 5823 | ||
| 5801 | 5824 | /// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor. |
| 5802 | 5825 | /// Returns null on failure. |
| 5803 | 5826 | pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom { |
| ... | ... | @@ -5834,7 +5857,7 @@ pub fn getTlvPtrAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom |
| 5834 | 5857 | /// Asserts output mode is executable. |
| 5835 | 5858 | pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc { |
| 5836 | 5859 | const entry_name = self.base.options.entry orelse "_main"; |
| 5837 | const global = self.globals.get(entry_name) orelse { | |
| 5860 | const global = self.getGlobal(entry_name) orelse { | |
| 5838 | 5861 | log.err("entrypoint '{s}' not found", .{entry_name}); |
| 5839 | 5862 | return error.MissingMainEntrypoint; |
| 5840 | 5863 | }; |
| ... | ... | @@ -6342,9 +6365,9 @@ fn logSymtab(self: *MachO) void { |
| 6342 | 6365 | } |
| 6343 | 6366 | |
| 6344 | 6367 | log.debug("globals table:", .{}); |
| 6345 | for (self.globals.keys()) |name, id| { | |
| 6346 | const value = self.globals.values()[id]; | |
| 6347 | log.debug(" {s} => %{d} in object({?d})", .{ name, value.sym_index, value.file }); | |
| 6368 | for (self.globals.items) |global| { | |
| 6369 | const name = self.getSymbolName(global); | |
| 6370 | log.debug(" {s} => %{d} in object({?d})", .{ name, global.sym_index, global.file }); | |
| 6348 | 6371 | } |
| 6349 | 6372 | |
| 6350 | 6373 | log.debug("GOT entries:", .{}); |
src/link/MachO/Atom.zig+3-3| ... | ... | @@ -272,7 +272,7 @@ pub fn parseRelocs(self: *Atom, relocs: []align(1) const macho.relocation_info, |
| 272 | 272 | subtractor = sym_loc; |
| 273 | 273 | } else { |
| 274 | 274 | const sym_name = context.macho_file.getSymbolName(sym_loc); |
| 275 | subtractor = context.macho_file.globals.get(sym_name).?; | |
| 275 | subtractor = context.macho_file.getGlobal(sym_name).?; | |
| 276 | 276 | } |
| 277 | 277 | // Verify that *_SUBTRACTOR is followed by *_UNSIGNED. |
| 278 | 278 | if (relocs.len <= i + 1) { |
| ... | ... | @@ -339,7 +339,7 @@ pub fn parseRelocs(self: *Atom, relocs: []align(1) const macho.relocation_info, |
| 339 | 339 | break :target sym_loc; |
| 340 | 340 | } else { |
| 341 | 341 | const sym_name = context.macho_file.getSymbolName(sym_loc); |
| 342 | break :target context.macho_file.globals.get(sym_name).?; | |
| 342 | break :target context.macho_file.getGlobal(sym_name).?; | |
| 343 | 343 | } |
| 344 | 344 | }; |
| 345 | 345 | const offset = @intCast(u32, rel.r_address - context.base_offset); |
| ... | ... | @@ -579,7 +579,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { |
| 579 | 579 | // If there is no atom for target, we still need to check for special, atom-less |
| 580 | 580 | // symbols such as `___dso_handle`. |
| 581 | 581 | const target_name = macho_file.getSymbolName(rel.target); |
| 582 | assert(macho_file.globals.contains(target_name)); | |
| 582 | assert(macho_file.getGlobal(target_name) != null); | |
| 583 | 583 | const atomless_sym = macho_file.getSymbol(rel.target); |
| 584 | 584 | log.debug(" | atomless target '{s}'", .{target_name}); |
| 585 | 585 | break :blk atomless_sym.n_value; |
src/link/MachO/DebugSymbols.zig+2-2| ... | ... | @@ -480,7 +480,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void { |
| 480 | 480 | if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip |
| 481 | 481 | const sym_loc = MachO.SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null }; |
| 482 | 482 | if (self.base.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip |
| 483 | if (self.base.globals.contains(self.base.getSymbolName(sym_loc))) continue; // global symbol is either an export or import, skip | |
| 483 | if (self.base.getGlobal(self.base.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip | |
| 484 | 484 | var out_sym = sym; |
| 485 | 485 | out_sym.n_strx = try self.strtab.insert(gpa, self.base.getSymbolName(sym_loc)); |
| 486 | 486 | try locals.append(out_sym); |
| ... | ... | @@ -489,7 +489,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void { |
| 489 | 489 | var exports = std.ArrayList(macho.nlist_64).init(gpa); |
| 490 | 490 | defer exports.deinit(); |
| 491 | 491 | |
| 492 | for (self.base.globals.values()) |global| { | |
| 492 | for (self.base.globals.items) |global| { | |
| 493 | 493 | const sym = self.base.getSymbol(global); |
| 494 | 494 | if (sym.undf()) continue; // import, skip |
| 495 | 495 | if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip |
src/link/MachO/dead_strip.zig+2-2| ... | ... | @@ -62,7 +62,7 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void |
| 62 | 62 | else => |other| { |
| 63 | 63 | assert(other == .Lib); |
| 64 | 64 | // Add exports as GC roots |
| 65 | for (macho_file.globals.values()) |global| { | |
| 65 | for (macho_file.globals.items) |global| { | |
| 66 | 66 | const sym = macho_file.getSymbol(global); |
| 67 | 67 | if (!sym.sect()) continue; |
| 68 | 68 | const atom = macho_file.getAtomForSymbol(global) orelse { |
| ... | ... | @@ -77,7 +77,7 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void |
| 77 | 77 | } |
| 78 | 78 | |
| 79 | 79 | // TODO just a temp until we learn how to parse unwind records |
| 80 | if (macho_file.globals.get("___gxx_personality_v0")) |global| { | |
| 80 | if (macho_file.getGlobal("___gxx_personality_v0")) |global| { | |
| 81 | 81 | if (macho_file.getAtomForSymbol(global)) |atom| { |
| 82 | 82 | _ = try roots.getOrPut(atom); |
| 83 | 83 | log.debug("adding root", .{}); |
src/translate_c.zig+18-2| ... | ... | @@ -5957,20 +5957,36 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 5957 | 5957 | return bytes[0..i]; |
| 5958 | 5958 | } |
| 5959 | 5959 | |
| 5960 | /// non-ASCII characters (c > 127) are also treated as non-printable by fmtSliceEscapeLower. | |
| 5961 | /// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape | |
| 5962 | /// non-ASCII characters so that the Zig source we output will itself be UTF-8. | |
| 5963 | fn escapeUnprintables(ctx: *Context, m: *MacroCtx) ![]const u8 { | |
| 5964 | const zigified = try zigifyEscapeSequences(ctx, m); | |
| 5965 | if (std.unicode.utf8ValidateSlice(zigified)) return zigified; | |
| 5966 | ||
| 5967 | const formatter = std.fmt.fmtSliceEscapeLower(zigified); | |
| 5968 | const encoded_size = @intCast(usize, std.fmt.count("{s}", .{formatter})); | |
| 5969 | var output = try ctx.arena.alloc(u8, encoded_size); | |
| 5970 | return std.fmt.bufPrint(output, "{s}", .{formatter}) catch |err| switch (err) { | |
| 5971 | error.NoSpaceLeft => unreachable, | |
| 5972 | else => |e| return e, | |
| 5973 | }; | |
| 5974 | } | |
| 5975 | ||
| 5960 | 5976 | fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 5961 | 5977 | const tok = m.next().?; |
| 5962 | 5978 | const slice = m.slice(); |
| 5963 | 5979 | switch (tok) { |
| 5964 | 5980 | .CharLiteral => { |
| 5965 | 5981 | if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) { |
| 5966 | return Tag.char_literal.create(c.arena, try zigifyEscapeSequences(c, m)); | |
| 5982 | return Tag.char_literal.create(c.arena, try escapeUnprintables(c, m)); | |
| 5967 | 5983 | } else { |
| 5968 | 5984 | const str = try std.fmt.allocPrint(c.arena, "0x{s}", .{std.fmt.fmtSliceHexLower(slice[1 .. slice.len - 1])}); |
| 5969 | 5985 | return Tag.integer_literal.create(c.arena, str); |
| 5970 | 5986 | } |
| 5971 | 5987 | }, |
| 5972 | 5988 | .StringLiteral => { |
| 5973 | return Tag.string_literal.create(c.arena, try zigifyEscapeSequences(c, m)); | |
| 5989 | return Tag.string_literal.create(c.arena, try escapeUnprintables(c, m)); | |
| 5974 | 5990 | }, |
| 5975 | 5991 | .IntegerLiteral, .FloatLiteral => { |
| 5976 | 5992 | return parseCNumLit(c, m); |
src/type.zig+3| ... | ... | @@ -2042,6 +2042,9 @@ pub const Type = extern union { |
| 2042 | 2042 | try writer.writeAll("fn("); |
| 2043 | 2043 | for (fn_info.param_types) |param_ty, i| { |
| 2044 | 2044 | if (i != 0) try writer.writeAll(", "); |
| 2045 | if (fn_info.paramIsComptime(i)) { | |
| 2046 | try writer.writeAll("comptime "); | |
| 2047 | } | |
| 2045 | 2048 | if (std.math.cast(u5, i)) |index| if (@truncate(u1, fn_info.noalias_bits >> index) != 0) { |
| 2046 | 2049 | try writer.writeAll("noalias "); |
| 2047 | 2050 | }; |
test/behavior/alignof.zig-1| ... | ... | @@ -13,7 +13,6 @@ const Foo = struct { |
| 13 | 13 | test "@alignOf(T) before referencing T" { |
| 14 | 14 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 15 | 15 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 16 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 17 | 16 | comptime try expect(@alignOf(Foo) != maxInt(usize)); |
| 18 | 17 | if (native_arch == .x86_64) { |
| 19 | 18 | comptime try expect(@alignOf(Foo) == 4); |
test/behavior/array.zig-5| ... | ... | @@ -175,7 +175,6 @@ test "nested arrays of integers" { |
| 175 | 175 | |
| 176 | 176 | test "implicit comptime in array type size" { |
| 177 | 177 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 178 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 179 | 178 | |
| 180 | 179 | var arr: [plusOne(10)]bool = undefined; |
| 181 | 180 | try expect(arr.len == 11); |
| ... | ... | @@ -245,7 +244,6 @@ const Sub = struct { b: u8 }; |
| 245 | 244 | const Str = struct { a: []Sub }; |
| 246 | 245 | test "set global var array via slice embedded in struct" { |
| 247 | 246 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 248 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 249 | 247 | |
| 250 | 248 | var s = Str{ .a = s_array[0..] }; |
| 251 | 249 | |
| ... | ... | @@ -298,7 +296,6 @@ fn testArrayByValAtComptime(b: [2]u8) u8 { |
| 298 | 296 | |
| 299 | 297 | test "comptime evaluating function that takes array by value" { |
| 300 | 298 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 301 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 302 | 299 | |
| 303 | 300 | const arr = [_]u8{ 1, 2 }; |
| 304 | 301 | const x = comptime testArrayByValAtComptime(arr); |
| ... | ... | @@ -427,7 +424,6 @@ test "anonymous literal in array" { |
| 427 | 424 | |
| 428 | 425 | test "access the null element of a null terminated array" { |
| 429 | 426 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 430 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 431 | 427 | |
| 432 | 428 | const S = struct { |
| 433 | 429 | fn doTheTest() !void { |
| ... | ... | @@ -484,7 +480,6 @@ test "sentinel element count towards the ABI size calculation" { |
| 484 | 480 | test "zero-sized array with recursive type definition" { |
| 485 | 481 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 486 | 482 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 487 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 488 | 483 | |
| 489 | 484 | const U = struct { |
| 490 | 485 | fn foo(comptime T: type, comptime n: usize) type { |
test/behavior/basic.zig-4| ... | ... | @@ -465,7 +465,6 @@ fn nine() u8 { |
| 465 | 465 | |
| 466 | 466 | test "struct inside function" { |
| 467 | 467 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 468 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 469 | 468 | |
| 470 | 469 | try testStructInFn(); |
| 471 | 470 | comptime try testStructInFn(); |
| ... | ... | @@ -514,7 +513,6 @@ var global_foo: *i32 = undefined; |
| 514 | 513 | |
| 515 | 514 | test "peer result location with typed parent, runtime condition, comptime prongs" { |
| 516 | 515 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 517 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 518 | 516 | |
| 519 | 517 | const S = struct { |
| 520 | 518 | fn doTheTest(arg: i32) i32 { |
| ... | ... | @@ -643,7 +641,6 @@ test "global constant is loaded with a runtime-known index" { |
| 643 | 641 | |
| 644 | 642 | test "multiline string literal is null terminated" { |
| 645 | 643 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 646 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 647 | 644 | |
| 648 | 645 | const s1 = |
| 649 | 646 | \\one |
| ... | ... | @@ -1060,7 +1057,6 @@ comptime { |
| 1060 | 1057 | |
| 1061 | 1058 | test "switch inside @as gets correct type" { |
| 1062 | 1059 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1063 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1064 | 1060 | |
| 1065 | 1061 | var a: u32 = 0; |
| 1066 | 1062 | var b: [2]u32 = undefined; |
test/behavior/bitcast.zig-1| ... | ... | @@ -138,7 +138,6 @@ test "@bitCast extern structs at runtime and comptime" { |
| 138 | 138 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 139 | 139 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 140 | 140 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 141 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 142 | 141 | |
| 143 | 142 | const Full = extern struct { |
| 144 | 143 | number: u16, |
test/behavior/cast.zig-10| ... | ... | @@ -523,7 +523,6 @@ fn testCastConstArrayRefToConstSlice() !void { |
| 523 | 523 | |
| 524 | 524 | test "peer type resolution: error and [N]T" { |
| 525 | 525 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 526 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 527 | 526 | |
| 528 | 527 | try expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); |
| 529 | 528 | comptime try expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); |
| ... | ... | @@ -548,7 +547,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { |
| 548 | 547 | test "single-item pointer of array to slice to unknown length pointer" { |
| 549 | 548 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 550 | 549 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 551 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 552 | 550 | |
| 553 | 551 | try testCastPtrOfArrayToSliceAndPtr(); |
| 554 | 552 | comptime try testCastPtrOfArrayToSliceAndPtr(); |
| ... | ... | @@ -578,7 +576,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void { |
| 578 | 576 | test "cast *[1][*]const u8 to [*]const ?[*]const u8" { |
| 579 | 577 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 580 | 578 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 581 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 582 | 579 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 583 | 580 | |
| 584 | 581 | const window_name = [1][*]const u8{"window name"}; |
| ... | ... | @@ -649,7 +646,6 @@ test "@floatCast cast down" { |
| 649 | 646 | test "peer type resolution: unreachable, error set, unreachable" { |
| 650 | 647 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 651 | 648 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 652 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 653 | 649 | |
| 654 | 650 | const Error = error{ |
| 655 | 651 | FileDescriptorAlreadyPresentInSet, |
| ... | ... | @@ -922,7 +918,6 @@ test "peer cast *[N:x]T to *[N]T" { |
| 922 | 918 | |
| 923 | 919 | test "peer cast [*:x]T to [*]T" { |
| 924 | 920 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 925 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 926 | 921 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 927 | 922 | |
| 928 | 923 | const S = struct { |
| ... | ... | @@ -964,7 +959,6 @@ test "peer cast [:x]T to [*:x]T" { |
| 964 | 959 | |
| 965 | 960 | test "peer type resolution implicit cast to return type" { |
| 966 | 961 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 967 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 968 | 962 | |
| 969 | 963 | const S = struct { |
| 970 | 964 | fn doTheTest() !void { |
| ... | ... | @@ -984,7 +978,6 @@ test "peer type resolution implicit cast to return type" { |
| 984 | 978 | |
| 985 | 979 | test "peer type resolution implicit cast to variable type" { |
| 986 | 980 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 987 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 988 | 981 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 989 | 982 | |
| 990 | 983 | const S = struct { |
| ... | ... | @@ -1009,7 +1002,6 @@ test "variable initialization uses result locations properly with regards to the |
| 1009 | 1002 | |
| 1010 | 1003 | test "cast between C pointer with different but compatible types" { |
| 1011 | 1004 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1012 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1013 | 1005 | |
| 1014 | 1006 | const S = struct { |
| 1015 | 1007 | fn foo(arg: [*]c_ushort) u16 { |
| ... | ... | @@ -1026,7 +1018,6 @@ test "cast between C pointer with different but compatible types" { |
| 1026 | 1018 | |
| 1027 | 1019 | test "peer type resolve string lit with sentinel-terminated mutable slice" { |
| 1028 | 1020 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1029 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1030 | 1021 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 1031 | 1022 | |
| 1032 | 1023 | var array: [4:0]u8 = undefined; |
| ... | ... | @@ -1079,7 +1070,6 @@ test "comptime float casts" { |
| 1079 | 1070 | test "pointer reinterpret const float to int" { |
| 1080 | 1071 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1081 | 1072 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 1082 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1083 | 1073 | |
| 1084 | 1074 | // The hex representation is 0x3fe3333333333303. |
| 1085 | 1075 | const float: f64 = 5.99999999999994648725e-01; |
test/behavior/comptime_memory.zig-2| ... | ... | @@ -87,7 +87,6 @@ fn bigToNativeEndian(comptime T: type, v: T) T { |
| 87 | 87 | test "type pun endianness" { |
| 88 | 88 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 89 | 89 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 90 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 91 | 90 | |
| 92 | 91 | comptime { |
| 93 | 92 | const StructOfBytes = extern struct { x: [4]u8 }; |
| ... | ... | @@ -398,7 +397,6 @@ test "offset field ptr by enclosing array element size" { |
| 398 | 397 | test "accessing reinterpreted memory of parent object" { |
| 399 | 398 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 400 | 399 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 401 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 402 | 400 | const S = extern struct { |
| 403 | 401 | a: f32, |
| 404 | 402 | b: [4]u8, |
test/behavior/const_slice_child.zig-1| ... | ... | @@ -9,7 +9,6 @@ var argv: [*]const [*]const u8 = undefined; |
| 9 | 9 | test "const slice child" { |
| 10 | 10 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 11 | 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 12 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 13 | 12 | |
| 14 | 13 | const strs = [_][*]const u8{ "one", "two", "three" }; |
| 15 | 14 | argv = &strs; |
test/behavior/enum.zig-6| ... | ... | @@ -606,7 +606,6 @@ fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void { |
| 606 | 606 | } |
| 607 | 607 | |
| 608 | 608 | test "enum with specified tag values" { |
| 609 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 610 | 609 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 611 | 610 | |
| 612 | 611 | try testEnumWithSpecifiedTagValues(MultipleChoice.C); |
| ... | ... | @@ -614,7 +613,6 @@ test "enum with specified tag values" { |
| 614 | 613 | } |
| 615 | 614 | |
| 616 | 615 | test "non-exhaustive enum" { |
| 617 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 618 | 616 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 619 | 617 | |
| 620 | 618 | const S = struct { |
| ... | ... | @@ -677,7 +675,6 @@ test "empty non-exhaustive enum" { |
| 677 | 675 | } |
| 678 | 676 | |
| 679 | 677 | test "single field non-exhaustive enum" { |
| 680 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 681 | 678 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 682 | 679 | |
| 683 | 680 | const S = struct { |
| ... | ... | @@ -741,7 +738,6 @@ test "cast integer literal to enum" { |
| 741 | 738 | } |
| 742 | 739 | |
| 743 | 740 | test "enum with specified and unspecified tag values" { |
| 744 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 745 | 741 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 746 | 742 | |
| 747 | 743 | try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); |
| ... | ... | @@ -925,7 +921,6 @@ test "enum literal casting to tagged union" { |
| 925 | 921 | const Bar = enum { A, B, C, D }; |
| 926 | 922 | |
| 927 | 923 | test "enum literal casting to error union with payload enum" { |
| 928 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 929 | 924 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 930 | 925 | |
| 931 | 926 | var bar: error{B}!Bar = undefined; |
| ... | ... | @@ -1132,7 +1127,6 @@ test "tag name functions are unique" { |
| 1132 | 1127 | |
| 1133 | 1128 | test "size of enum with only one tag which has explicit integer tag type" { |
| 1134 | 1129 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 1135 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 1136 | 1130 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1137 | 1131 | |
| 1138 | 1132 | const E = enum(u8) { nope = 10 }; |
test/behavior/error.zig-4| ... | ... | @@ -222,7 +222,6 @@ fn testErrorSetType() !void { |
| 222 | 222 | |
| 223 | 223 | test "explicit error set cast" { |
| 224 | 224 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 225 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 226 | 225 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 227 | 226 | |
| 228 | 227 | try testExplicitErrorSetCast(Set1.A); |
| ... | ... | @@ -282,7 +281,6 @@ test "inferred empty error set comptime catch" { |
| 282 | 281 | } |
| 283 | 282 | |
| 284 | 283 | test "error union peer type resolution" { |
| 285 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 286 | 284 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 287 | 285 | |
| 288 | 286 | try testErrorUnionPeerTypeResolution(1); |
| ... | ... | @@ -327,7 +325,6 @@ fn foo3(b: usize) Error!usize { |
| 327 | 325 | |
| 328 | 326 | test "error: Infer error set from literals" { |
| 329 | 327 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 330 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 331 | 328 | |
| 332 | 329 | _ = nullLiteral("n") catch |err| handleErrors(err); |
| 333 | 330 | _ = floatLiteral("n") catch |err| handleErrors(err); |
| ... | ... | @@ -700,7 +697,6 @@ test "ret_ptr doesn't cause own inferred error set to be resolved" { |
| 700 | 697 | |
| 701 | 698 | test "simple else prong allowed even when all errors handled" { |
| 702 | 699 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 703 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 704 | 700 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 705 | 701 | |
| 706 | 702 | const S = struct { |
test/behavior/eval.zig-10| ... | ... | @@ -69,7 +69,6 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | 71 | test "constant expressions" { |
| 72 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 73 | 72 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 74 | 73 | |
| 75 | 74 | var array: [array_size]u8 = undefined; |
| ... | ... | @@ -138,7 +137,6 @@ test "pointer to type" { |
| 138 | 137 | test "a type constructed in a global expression" { |
| 139 | 138 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 140 | 139 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 141 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 142 | 140 | |
| 143 | 141 | var l: List = undefined; |
| 144 | 142 | l.array[0] = 10; |
| ... | ... | @@ -338,7 +336,6 @@ fn doesAlotT(comptime T: type, value: usize) T { |
| 338 | 336 | } |
| 339 | 337 | |
| 340 | 338 | test "@setEvalBranchQuota at same scope as generic function call" { |
| 341 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 342 | 339 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 343 | 340 | |
| 344 | 341 | try expect(doesAlotT(u32, 2) == 2); |
| ... | ... | @@ -565,7 +562,6 @@ test "inlined loop has array literal with elided runtime scope on first iteratio |
| 565 | 562 | } |
| 566 | 563 | |
| 567 | 564 | test "ptr to local array argument at comptime" { |
| 568 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 569 | 565 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 570 | 566 | |
| 571 | 567 | comptime { |
| ... | ... | @@ -806,7 +802,6 @@ test "array concatenation sets the sentinel - value" { |
| 806 | 802 | test "array concatenation sets the sentinel - pointer" { |
| 807 | 803 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 808 | 804 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 809 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 810 | 805 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 811 | 806 | |
| 812 | 807 | var a = [2]u3{ 1, 7 }; |
| ... | ... | @@ -956,7 +951,6 @@ test "const local with comptime init through array init" { |
| 956 | 951 | |
| 957 | 952 | test "closure capture type of runtime-known parameter" { |
| 958 | 953 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 959 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 960 | 954 | |
| 961 | 955 | const S = struct { |
| 962 | 956 | fn b(c: anytype) !void { |
| ... | ... | @@ -1074,7 +1068,6 @@ test "comptime break operand passing through runtime switch converted to runtime |
| 1074 | 1068 | |
| 1075 | 1069 | test "no dependency loop for alignment of self struct" { |
| 1076 | 1070 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1077 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1078 | 1071 | |
| 1079 | 1072 | const S = struct { |
| 1080 | 1073 | fn doTheTest() !void { |
| ... | ... | @@ -1111,7 +1104,6 @@ test "no dependency loop for alignment of self struct" { |
| 1111 | 1104 | |
| 1112 | 1105 | test "no dependency loop for alignment of self bare union" { |
| 1113 | 1106 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1114 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1115 | 1107 | |
| 1116 | 1108 | const S = struct { |
| 1117 | 1109 | fn doTheTest() !void { |
| ... | ... | @@ -1148,7 +1140,6 @@ test "no dependency loop for alignment of self bare union" { |
| 1148 | 1140 | |
| 1149 | 1141 | test "no dependency loop for alignment of self tagged union" { |
| 1150 | 1142 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1151 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1152 | 1143 | |
| 1153 | 1144 | const S = struct { |
| 1154 | 1145 | fn doTheTest() !void { |
| ... | ... | @@ -1336,7 +1327,6 @@ test "lazy sizeof is resolved in division" { |
| 1336 | 1327 | } |
| 1337 | 1328 | |
| 1338 | 1329 | test "lazy value is resolved as slice operand" { |
| 1339 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1340 | 1330 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1341 | 1331 | |
| 1342 | 1332 | const A = struct { a: u32 }; |
test/behavior/field_parent_ptr.zig-2| ... | ... | @@ -2,7 +2,6 @@ const expect = @import("std").testing.expect; |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | test "@fieldParentPtr non-first field" { |
| 5 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 6 | 5 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 7 | 6 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 8 | 7 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| ... | ... | @@ -11,7 +10,6 @@ test "@fieldParentPtr non-first field" { |
| 11 | 10 | } |
| 12 | 11 | |
| 13 | 12 | test "@fieldParentPtr first field" { |
| 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 15 | 13 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 16 | 14 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 17 | 15 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
test/behavior/for.zig-4| ... | ... | @@ -5,7 +5,6 @@ const expectEqual = std.testing.expectEqual; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | |
| 7 | 7 | test "continue in for loop" { |
| 8 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 9 | 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 10 | 9 | |
| 11 | 10 | const array = [_]i32{ 1, 2, 3, 4, 5 }; |
| ... | ... | @@ -130,7 +129,6 @@ test "for with null and T peer types and inferred result location type" { |
| 130 | 129 | } |
| 131 | 130 | |
| 132 | 131 | test "2 break statements and an else" { |
| 133 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 134 | 132 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 135 | 133 | |
| 136 | 134 | const S = struct { |
| ... | ... | @@ -177,7 +175,6 @@ fn mangleString(s: []u8) void { |
| 177 | 175 | } |
| 178 | 176 | |
| 179 | 177 | test "for copies its payload" { |
| 180 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 181 | 178 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 182 | 179 | |
| 183 | 180 | const S = struct { |
| ... | ... | @@ -213,7 +210,6 @@ test "for on slice with allowzero ptr" { |
| 213 | 210 | |
| 214 | 211 | test "else continue outer for" { |
| 215 | 212 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 216 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 217 | 213 | |
| 218 | 214 | var i: usize = 6; |
| 219 | 215 | var buf: [5]u8 = undefined; |
test/behavior/generics.zig-1| ... | ... | @@ -91,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 { |
| 91 | 91 | |
| 92 | 92 | test "type constructed by comptime function call" { |
| 93 | 93 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 94 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 95 | 94 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 96 | 95 | |
| 97 | 96 | var l: SimpleList(10) = undefined; |
test/behavior/merge_error_sets.zig-1| ... | ... | @@ -12,7 +12,6 @@ fn foo() C!void { |
| 12 | 12 | } |
| 13 | 13 | |
| 14 | 14 | test "merge error sets" { |
| 15 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 16 | 15 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 17 | 16 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 18 | 17 | if (foo()) { |
test/behavior/pointers.zig-14| ... | ... | @@ -17,8 +17,6 @@ fn testDerefPtr() !void { |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | test "pointer arithmetic" { |
| 20 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 21 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 22 | 20 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 23 | 21 | |
| 24 | 22 | var ptr: [*]const u8 = "abcd"; |
| ... | ... | @@ -65,8 +63,6 @@ test "initialize const optional C pointer to null" { |
| 65 | 63 | } |
| 66 | 64 | |
| 67 | 65 | test "assigning integer to C pointer" { |
| 68 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 69 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 70 | 66 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 71 | 67 | |
| 72 | 68 | var x: i32 = 0; |
| ... | ... | @@ -83,8 +79,6 @@ test "assigning integer to C pointer" { |
| 83 | 79 | } |
| 84 | 80 | |
| 85 | 81 | test "C pointer comparison and arithmetic" { |
| 86 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 87 | ||
| 88 | 82 | const S = struct { |
| 89 | 83 | fn doTheTest() !void { |
| 90 | 84 | var ptr1: [*c]u32 = 0; |
| ... | ... | @@ -150,7 +144,6 @@ test "peer type resolution with C pointer and const pointer" { |
| 150 | 144 | } |
| 151 | 145 | |
| 152 | 146 | test "implicit casting between C pointer and optional non-C pointer" { |
| 153 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 154 | 147 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 155 | 148 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 156 | 149 | |
| ... | ... | @@ -281,8 +274,6 @@ test "array initialization types" { |
| 281 | 274 | |
| 282 | 275 | test "null terminated pointer" { |
| 283 | 276 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 284 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 285 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 286 | 277 | |
| 287 | 278 | const S = struct { |
| 288 | 279 | fn doTheTest() !void { |
| ... | ... | @@ -299,8 +290,6 @@ test "null terminated pointer" { |
| 299 | 290 | |
| 300 | 291 | test "allow any sentinel" { |
| 301 | 292 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 302 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 303 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 304 | 293 | |
| 305 | 294 | const S = struct { |
| 306 | 295 | fn doTheTest() !void { |
| ... | ... | @@ -315,8 +304,6 @@ test "allow any sentinel" { |
| 315 | 304 | |
| 316 | 305 | test "pointer sentinel with enums" { |
| 317 | 306 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 318 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 319 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 320 | 307 | |
| 321 | 308 | const S = struct { |
| 322 | 309 | const Number = enum { |
| ... | ... | @@ -337,7 +324,6 @@ test "pointer sentinel with enums" { |
| 337 | 324 | test "pointer sentinel with optional element" { |
| 338 | 325 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 339 | 326 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 340 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 341 | 327 | |
| 342 | 328 | const S = struct { |
| 343 | 329 | fn doTheTest() !void { |
test/behavior/ptrcast.zig-3| ... | ... | @@ -4,7 +4,6 @@ const expect = std.testing.expect; |
| 4 | 4 | const native_endian = builtin.target.cpu.arch.endian(); |
| 5 | 5 | |
| 6 | 6 | test "reinterpret bytes as integer with nonzero offset" { |
| 7 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 8 | 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 9 | 8 | |
| 10 | 9 | try testReinterpretBytesAsInteger(); |
| ... | ... | @@ -39,7 +38,6 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void { |
| 39 | 38 | } |
| 40 | 39 | |
| 41 | 40 | test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" { |
| 42 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 43 | 41 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 44 | 42 | |
| 45 | 43 | try testReinterpretStructWrappedBytesAsInteger(); |
| ... | ... | @@ -179,7 +177,6 @@ test "lower reinterpreted comptime field ptr" { |
| 179 | 177 | } |
| 180 | 178 | |
| 181 | 179 | test "reinterpret struct field at comptime" { |
| 182 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 183 | 180 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 184 | 181 | |
| 185 | 182 | const numNative = comptime Bytes.init(0x12345678); |
test/behavior/sizeof_and_typeof.zig-2| ... | ... | @@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" { |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | 20 | test "@TypeOf() with multiple arguments" { |
| 21 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 22 | 21 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 23 | 22 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 24 | 23 | { |
| ... | ... | @@ -77,7 +76,6 @@ const P = packed struct { |
| 77 | 76 | }; |
| 78 | 77 | |
| 79 | 78 | test "@offsetOf" { |
| 80 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 81 | 79 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 82 | 80 | |
| 83 | 81 | // Packed structs have fixed memory layout |
test/behavior/slice.zig-3| ... | ... | @@ -28,7 +28,6 @@ comptime { |
| 28 | 28 | |
| 29 | 29 | test "slicing" { |
| 30 | 30 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 31 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 32 | 31 | |
| 33 | 32 | var array: [20]i32 = undefined; |
| 34 | 33 | |
| ... | ... | @@ -269,7 +268,6 @@ fn sliceSum(comptime q: []const u8) i32 { |
| 269 | 268 | |
| 270 | 269 | test "slice type with custom alignment" { |
| 271 | 270 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 272 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 273 | 271 | |
| 274 | 272 | const LazilyResolvedType = struct { |
| 275 | 273 | anything: i32, |
| ... | ... | @@ -283,7 +281,6 @@ test "slice type with custom alignment" { |
| 283 | 281 | |
| 284 | 282 | test "obtaining a null terminated slice" { |
| 285 | 283 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 286 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 287 | 284 | |
| 288 | 285 | // here we have a normal array |
| 289 | 286 | var buf: [50]u8 = undefined; |
test/behavior/struct.zig-8| ... | ... | @@ -10,7 +10,6 @@ top_level_field: i32, |
| 10 | 10 | |
| 11 | 11 | test "top level fields" { |
| 12 | 12 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 13 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 14 | 13 | |
| 15 | 14 | var instance = @This(){ |
| 16 | 15 | .top_level_field = 1234, |
| ... | ... | @@ -104,7 +103,6 @@ fn testMutation(foo: *StructFoo) void { |
| 104 | 103 | |
| 105 | 104 | test "struct byval assign" { |
| 106 | 105 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 107 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 108 | 106 | |
| 109 | 107 | var foo1: StructFoo = undefined; |
| 110 | 108 | var foo2: StructFoo = undefined; |
| ... | ... | @@ -240,7 +238,6 @@ test "usingnamespace within struct scope" { |
| 240 | 238 | |
| 241 | 239 | test "struct field init with catch" { |
| 242 | 240 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 243 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 244 | 241 | |
| 245 | 242 | const S = struct { |
| 246 | 243 | fn doTheTest() !void { |
| ... | ... | @@ -281,7 +278,6 @@ const Val = struct { |
| 281 | 278 | test "struct point to self" { |
| 282 | 279 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 283 | 280 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 284 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 285 | 281 | |
| 286 | 282 | var root: Node = undefined; |
| 287 | 283 | root.val.x = 1; |
| ... | ... | @@ -297,7 +293,6 @@ test "struct point to self" { |
| 297 | 293 | |
| 298 | 294 | test "void struct fields" { |
| 299 | 295 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 300 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 301 | 296 | |
| 302 | 297 | const foo = VoidStructFieldsFoo{ |
| 303 | 298 | .a = void{}, |
| ... | ... | @@ -761,7 +756,6 @@ test "packed struct with u0 field access" { |
| 761 | 756 | } |
| 762 | 757 | |
| 763 | 758 | test "access to global struct fields" { |
| 764 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 765 | 759 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 766 | 760 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 767 | 761 | |
| ... | ... | @@ -1260,7 +1254,6 @@ test "typed init through error unions and optionals" { |
| 1260 | 1254 | |
| 1261 | 1255 | test "initialize struct with empty literal" { |
| 1262 | 1256 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1263 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1264 | 1257 | |
| 1265 | 1258 | const S = struct { x: i32 = 1234 }; |
| 1266 | 1259 | var s: S = .{}; |
| ... | ... | @@ -1362,7 +1355,6 @@ test "store to comptime field" { |
| 1362 | 1355 | |
| 1363 | 1356 | test "struct field init value is size of the struct" { |
| 1364 | 1357 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1365 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1366 | 1358 | |
| 1367 | 1359 | const namespace = struct { |
| 1368 | 1360 | const S = extern struct { |
test/behavior/switch.zig-2| ... | ... | @@ -348,7 +348,6 @@ test "switch on const enum with var" { |
| 348 | 348 | } |
| 349 | 349 | |
| 350 | 350 | test "anon enum literal used in switch on union enum" { |
| 351 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 352 | 351 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 353 | 352 | |
| 354 | 353 | const Foo = union(enum) { |
| ... | ... | @@ -490,7 +489,6 @@ test "switch prongs with error set cases make a new error set type for capture v |
| 490 | 489 | } |
| 491 | 490 | |
| 492 | 491 | test "return result loc and then switch with range implicit casted to error union" { |
| 493 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 494 | 492 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 495 | 493 | |
| 496 | 494 | const S = struct { |
test/behavior/this.zig-1| ... | ... | @@ -25,7 +25,6 @@ test "this refer to module call private fn" { |
| 25 | 25 | } |
| 26 | 26 | |
| 27 | 27 | test "this refer to container" { |
| 28 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 29 | 28 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 30 | 29 | |
| 31 | 30 | var pt: Point(i32) = undefined; |
test/behavior/translate_c_macros.zig+12| ... | ... | @@ -5,6 +5,7 @@ const expectEqual = std.testing.expectEqual; |
| 5 | 5 | const expectEqualStrings = std.testing.expectEqualStrings; |
| 6 | 6 | |
| 7 | 7 | const h = @cImport(@cInclude("behavior/translate_c_macros.h")); |
| 8 | const latin1 = @cImport(@cInclude("behavior/translate_c_macros_not_utf8.h")); | |
| 8 | 9 | |
| 9 | 10 | test "casting to void with a macro" { |
| 10 | 11 | h.IGNORE_ME_1(42); |
| ... | ... | @@ -134,3 +135,14 @@ test "string literal macro with embedded tab character" { |
| 134 | 135 | |
| 135 | 136 | try expectEqualStrings("hello\t", h.EMBEDDED_TAB); |
| 136 | 137 | } |
| 138 | ||
| 139 | test "string and char literals that are not UTF-8 encoded. Issue #12784" { | |
| 140 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 141 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 142 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 143 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 144 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 145 | ||
| 146 | try expectEqual(@as(u8, '\xA9'), latin1.UNPRINTABLE_CHAR); | |
| 147 | try expectEqualStrings("\xA9\xA9\xA9", latin1.UNPRINTABLE_STRING); | |
| 148 | } |
test/behavior/translate_c_macros_not_utf8.h created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | // Note: This file is encoded with ISO/IEC 8859-1 (latin1), not UTF-8. | |
| 2 | // Do not change the encoding | |
| 3 | ||
| 4 | #define UNPRINTABLE_STRING "���" | |
| 5 | #define UNPRINTABLE_CHAR '�' |
test/behavior/try.zig-1| ... | ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | |
| 5 | 5 | test "try on error union" { |
| 6 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 7 | 6 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 8 | 7 | |
| 9 | 8 | try tryOnErrorUnionImpl(); |
test/behavior/typename.zig+2-2| ... | ... | @@ -122,7 +122,7 @@ test "top level decl" { |
| 122 | 122 | ); |
| 123 | 123 | // generic fn |
| 124 | 124 | try expectEqualStrings( |
| 125 | "fn(type) type", | |
| 125 | "fn(comptime type) type", | |
| 126 | 126 | @typeName(@TypeOf(TypeFromFn)), |
| 127 | 127 | ); |
| 128 | 128 | } |
| ... | ... | @@ -244,5 +244,5 @@ test "comptime parameters not converted to anytype in function type" { |
| 244 | 244 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 245 | 245 | |
| 246 | 246 | const T = fn (fn (type) void, void) void; |
| 247 | try expectEqualStrings("fn(fn(type) void, void) void", @typeName(T)); | |
| 247 | try expectEqualStrings("fn(comptime fn(comptime type) void, void) void", @typeName(T)); | |
| 248 | 248 | } |
test/behavior/union.zig-1| ... | ... | @@ -92,7 +92,6 @@ const FooExtern = extern union { |
| 92 | 92 | }; |
| 93 | 93 | |
| 94 | 94 | test "basic extern unions" { |
| 95 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 96 | 95 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 97 | 96 | |
| 98 | 97 | var foo = FooExtern{ .int = 1 }; |
test/behavior/usingnamespace.zig-1| ... | ... | @@ -58,7 +58,6 @@ test "two files usingnamespace import each other" { |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | 60 | test { |
| 61 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 62 | 61 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 63 | 62 | |
| 64 | 63 | const AA = struct { |
test/behavior/while.zig-1| ... | ... | @@ -175,7 +175,6 @@ test "while with optional as condition with else" { |
| 175 | 175 | |
| 176 | 176 | test "while with error union condition" { |
| 177 | 177 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 178 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 179 | 178 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 180 | 179 | |
| 181 | 180 | numbers_left = 10; |
test/cases/compile_errors/comptime_param_coersion.zig created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | pub export fn entry() void { | |
| 2 | comptime var x: fn (comptime i32, comptime i32) void = undefined; | |
| 3 | x = bar; | |
| 4 | } | |
| 5 | pub export fn entry1() void { | |
| 6 | comptime var x: fn (i32, i32) void = undefined; | |
| 7 | x = foo; | |
| 8 | } | |
| 9 | ||
| 10 | fn foo(comptime _: i32, comptime _: i32) void {} | |
| 11 | fn bar(comptime _: i32, _: i32) void {} | |
| 12 | ||
| 13 | // error | |
| 14 | // backend=stage2 | |
| 15 | // target=native | |
| 16 | // | |
| 17 | // :3:9: error: expected type 'fn(comptime i32, comptime i32) void', found 'fn(comptime i32, i32) void' | |
| 18 | // :3:9: note: non-comptime parameter 1 cannot cast into a comptime parameter | |
| 19 | // :7:9: error: expected type 'fn(i32, i32) void', found 'fn(comptime i32, comptime i32) void' | |
| 20 | // :7:9: note: generic function cannot cast into a non-generic function |