diff --git a/examples/cdsl/Example.core_desc b/examples/cdsl/Example.core_desc index 64ae21f2..a9c130b0 100644 --- a/examples/cdsl/Example.core_desc +++ b/examples/cdsl/Example.core_desc @@ -5,9 +5,8 @@ InstructionSet XExample extends RV32I { instructions { // TODO: remove/replace prefix CV_SUBINCACC { - // encoding: 7'b0101000 :: 5'b00000 :: rs1[4:0] :: 3'b011 :: rd[4:0] :: 7'b0101011; encoding: 7'b0101000 :: rs2[4:0] :: rs1[4:0] :: 3'b011 :: rd[4:0] :: 7'b0101011; - assembly: "{name(rd)}, {name(rs1)}"; + assembly: "{name(rd)}, {name(rs1)}, {name(rs2)}"; behavior: { if (rd != 0) { X[rd] += X[rs1] - X[rs2] + 1; diff --git a/examples/cdsl/RV32P.core_desc b/examples/cdsl/RV32P.core_desc new file mode 100644 index 00000000..31bd0e0a --- /dev/null +++ b/examples/cdsl/RV32P.core_desc @@ -0,0 +1,808 @@ +// import "RISCVBase.core_desc" +import "rv_base/RV32I.core_desc" + +InstructionSet RV32Zpn extends RV32I { + // architectural_state { + // // unsigned<32> VXSAT_ADDR__ = 0x009; + // // unsigned<32>& VXSAT_CSR__ = CSR[VXSAT_ADDR__]; + // } + instructions { + // This instruction adds the 8-bit integer elements in Rs1 with the 8-bit integer elements in Rs2, + // and then writes the 8-bit element results to Rd. + // Rd.B[x] = Rs1.B[x] + Rs2.B[x]; + ADD8 { + encoding: 7'b0100100 :: rs2[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + unsigned<8> rd_val0 = rs1_val[7:0] + rs2_val[7:0]; + unsigned<8> rd_val1 = rs1_val[15:8] + rs2_val[15:8]; + unsigned<8> rd_val2 = rs1_val[23:16] + rs2_val[23:16]; + unsigned<8> rd_val3 = rs1_val[31:24] + rs2_val[31:24]; + unsigned<32> rd_val = rd_val3 :: rd_val2 :: rd_val1 :: rd_val0; + X[rd] = rd_val; + } + } + } + // This instruction adds the 16-bit integer elements in Rs1 with the 16-bit integer elements in Rs2, + // and then writes the 16-bit element results to Rd. + // Rd.H[x] = Rs1.H[x] + Rs2.H[x]; + ADD16 { + encoding: 7'b0100000 :: rs2[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + unsigned<16> rd_val0 = rs1_val[15:0] + rs2_val[15:0]; + unsigned<16> rd_val1 = rs1_val[31:16] + rs2_val[31:16]; + unsigned<32> rd_val = rd_val1 :: rd_val0; + X[rd] = rd_val; + } + } + } + // AVE + // BITREV + // BITREVI + // BPICK + // CLROV + // CLRS8 + // CLRS16 + // CLRS32 + // CLZ8 + // CLZ16 + // CMPEQ8 + // CMPEQ16 + // CRAS16 + // CRSA16 + // INSB + // KABS8 + // KABS16 + // KABSW + // KADD8 + // KADD16 + // KADDH + // KADDW + // KCRAS16 + // KCRSA16 + // KDMBB + // KDMBT + // KDMTT + // KDMABB + // KDMABT + // KDMATT + // KHM8 + // KHMX8 + // KHM16 + // KHMX16 + // KHMBB + // KHMBT + // KHMTT + // KMABB + // KMABT + // KMATT + // TODO + // mula32[x] = Rs1.W[x].H[1] s* Rs2.W[x].H[1]; // KMADA + // mulb32[x] = Rs1.W[x].H[0] s* Rs2.W[x].H[0]; // + // res34[x] = SE34(Rd.W[x]) + SE34(mula32[x]) + SE34(mulb32[x]); + // if (res34[x] s> (2^31)-1) { + // res34[x] = (2^31)-1; + // OV = 1; + // } else if (res34[x] s< -2^31) { + // res34[x] = -2^31; + // OV = 1; + // } + // Rd.W[x] = res34[x].W[0]; + // for RV32: x=0 + // for RV64: x=1..0 + // KMADA { + // encoding: 7'b0100100 :: rs2[4:0] :: rs1[4:0] :: 3'b001 :: rd[4:0] :: 7'b1110111; + // assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + // behavior: { + // if(rd != 0) { + // unsigned<32> rs1_val = X[rs1]; + // unsigned<32> rs2_val = X[rs2]; + // signed<32> rd_val = X[rd]; + // signed<16> rs1_val_lo = rs1_val[15:0]; + // signed<16> rs1_val_hi = rs1_val[31:16]; + // signed<16> rs2_val_lo = rs2_val[15:0]; + // signed<16> rs2_val_hi = rs2_val[31:16]; + // signed<32> mula32 = rs1_val_hi * rs2_val_hi; + // signed<32> mulb32 = rs1_val_lo * rs2_val_lo; + // // signed<34> res34 = (signed<34>)rd_val + (signed<34>)mula32 + (signed<34>)mulb32; // does not work on etiss + // signed<64> res34 = (signed<64>)rd_val + (signed<64>)mula32 + (signed<64>)mulb32; + // if (res34 >= (signed<64>)(1 << 32)) { // broken? + // // if (res34 >= (signed<64>)0 && res34 >= (1 << 32)) { + // res34 = (1 << 32) - 1; + // VXSAT_CSR__ = VXSAT_CSR__ | 1'b1; // set OV bit + // // } else if (res34 < -(1 << 32)) { // broken? + // // } else if (res34 < 0 && (unsigned<64>)(-res34) > (1 << 32)) { + // } else if (res34 < -(1 << 32)) { + // res34 = -(1 << 32); + // VXSAT_CSR = VXSAT_CSR__ | 1'b1; // set OV bit + // } + // X[rd] = res34[31:0]; + // } + // } + // } + // TODO + // mula32[x] = Rs1.W[x].H[1] s* Rs2.W[x].H[0]; // KMAXDA + // mulb32[x] = Rs1.W[x].H[0] s* Rs2.W[x].H[1]; // + // res34[x] = SE34(Rd.W[x]) + SE34(mula32[x]) + SE34(mulb32[x]); + // if (res34[x] s> (2^31)-1) { + // res34[x] = (2^31)-1; + // OV = 1; + // } else if (res34[x] s< -2^31) { + // res34[x] = -2^31; + // OV = 1; + // } + // Rd.W[x] = res34[x].W[0]; + // for RV32: x=0 + // for RV64: x=1..0 + // KMAXDA { + // encoding: 7'b0100101 :: rs2[4:0] :: rs1[4:0] :: 3'b001 :: rd[4:0] :: 7'b1110111; + // assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + // behavior: { + // if(rd != 0) { + // unsigned<32> rs1_val = X[rs1]; + // unsigned<32> rs2_val = X[rs2]; + // signed<32> rd_val = X[rd]; + // signed<16> rs1_val_lo = rs1_val[15:0]; + // signed<16> rs1_val_hi = rs1_val[31:16]; + // signed<16> rs2_val_lo = rs2_val[15:0]; + // signed<16> rs2_val_hi = rs2_val[31:16]; + // signed<32> mula32 = rs1_val_hi * rs2_val_lo; + // signed<32> mulb32 = rs1_val_lo * rs2_val_hi; + // // signed<34> res34 = (signed<34>)rd_val + (signed<34>)mula32 + (signed<34>)mulb32; // does not work on etiss + // signed<64> res34 = (signed<64>)rd_val + (signed<64>)mula32 + (signed<64>)mulb32; + // if (res34 >= (signed<64>)(1 << 32)) { + // res34 = (1 << 32) - 1; + // VXSAT_CSR__ = VXSAT_CSR__ | 1'b1; // set OV bit + // } else if (res34 < -(1 << 32)) { + // res34 = -(1 << 32); + // VXSAT_CSR__ = VXSAT_CSR__ | 1'b1; // set OV bit + // } + // X[rd] = res34[31:0]; + // } + // } + // } + + // KMADS + // KMADRS + // KMAXDS + // KMDA + // KMXDA + // KMMAC + // KMMAC.u + // KMMAWB + // KMMAWB.u + // KMMAWB2 + // KMMAWB2.u + // KMMAWT + // KMMAWT.u + // KMMAWT2 + // KMMAWT2.u + // KMMSB + // KMMSB.u + // KMMWB2 + // KMMWB2.u + // KMMWT2 + // KMMWT2.u + // KMSDA + // KMSXDA + // KSLLW + // KSLLIW + // KSLL8 + // KSLLI8 + // KSLL16 + // KSLLI16 + // KSLRA8 + // KSLRA8.u + // KSLRA16 + // KSLRA16.u + // KSLRAW + // KSLRAW.U + // KSTAS16 + // KSTSA16 + // KSUB8 + // KSUB16 + // KSUBH + // KSUBW + // KWMMUL + // KWMMUL.u + // TODO + // Mresult = Rs1 * Rs2; + // Rd = Rd + Mresult.W[0]; // overflow ignored + MADDR32 { + encoding: 7'b1100010 :: rs2[4:0] :: rs1[4:0] :: 3'b001 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + signed<32> rs1_val = X[rs1]; + signed<32> rs2_val = X[rs2]; + signed<64> temp = rs1_val * rs2_val; + signed<32> rd_val = X[rd] + temp[31:0]; + X[rd] = rd_val; + } + } + } + // MAXW + // MINW + // MSUBR32 + // PBSAD + // PBSADA + // PKBB16 + // PKBT16 + // PKTT16 + // PKTB16 + // RADD8 + // RADD16 + // RADDW + // RCRAS16 + // RCRSA16 + // RDOV + // RSTAS16 + // RSTSA16 + // RSUB8 + // RSUB16 + // RSUBW + // SCLIP8 + // SCLIP16 + // SCLIP32 + // This instruction compares the 8-bit signed integer elements in Rs1 with the 8-bit signed integer elements in Rs2 + // to see if the one in Rs1 is less than or equal to the one in Rs2. + // If it is true, the result is 0xFF; otherwise, the result is 0x0. The element comparison results are written to Rd + // Rd.B[x] = (Rs1.B[x] <= Rs2.B[x]) ? 0xff : 0x0; + SCMPLE8 { + encoding: 7'b0001111 :: rs2[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + unsigned<8> rd_val0 = rs1_val[7:0] <= rs2_val[7:0] ? 0xff : 0x00; + unsigned<8> rd_val1 = rs1_val[15:8] <= rs2_val[15:8] ? 0xff : 0x00; + unsigned<8> rd_val2 = rs1_val[23:16] <= rs2_val[23:16] ? 0xff : 0x00; + unsigned<8> rd_val3 = rs1_val[31:24] <= rs2_val[31:24] ? 0xff : 0x00; + unsigned<32> rd_val = rd_val3 :: rd_val2 :: rd_val1 :: rd_val0; + X[rd] = rd_val; + } + } + } + // SCMPLE16 + // SCMPLT8 + // SCMPLT16 + // The 8-bit elements in Rs1 are left-shifted logically. And the results are written to Rd. + // The shifted out bits are filled with zero and the shift amount is specified by the low-order 3-bits of the value in the Rs2 register. + // Rd.B[x] = Rs1.B[x] << Rs2[2:0]; + SLL8 { + encoding: 7'b0101110 :: rs2[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + unsigned<8> rd_val0 = rs1_val[7:0] << rs2_val[2:0]; + unsigned<8> rd_val1 = rs1_val[15:8] << rs2_val[10:8]; + unsigned<8> rd_val2 = rs1_val[23:16] << rs2_val[18:16]; + unsigned<8> rd_val3 = rs1_val[31:24] << rs2_val[26:24]; + unsigned<32> rd_val = rd_val3 :: rd_val2 :: rd_val1 :: rd_val0; + X[rd] = rd_val; + } + } + } + // SLLI8 + // SLL16 + // SLLI16 + // This instruction multiplies the four signed 8-bit elements of 32-bit chunks of Rs1 + // with the four signed 8-bit elements of 32-bit chunks of Rs2 and then adds the four results together + // with the signed content of the corresponding 32-bit chunks of Rd. + // The final results are written back to the corresponding 32-bit chunks in Rd. + // res[x] = Rd.W[x] + (Rs1.W[x].B[3] s* Rs2.W[x].B[3]) + (Rs1.W[x].B[2] s* Rs2.W[x].B[2]) + // + (Rs1.W[x].B[1] s* Rs2.W[x].B[1]) + (Rs1.W[x].B[0] s* Rs2.W[x].B[0]); + // Rd.W[x] = res[x]; + SMAQA { + encoding: 7'b1100100 :: rs2[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + signed<32> rd_val = X[rd]; + signed<32> tmp_val0 = (signed<8>)rs1_val[7:0] * (signed<8>)rs2_val[7:0] + (signed<8>)rd_val[7:0]; + signed<32> tmp_val1 = (signed<8>)rs1_val[15:8] * (signed<8>)rs2_val[15:8] + (signed<8>)rd_val[15:8]; + signed<32> tmp_val2 = (signed<8>)rs1_val[23:16] * (signed<8>)rs2_val[23:16] + (signed<8>)rd_val[23:16]; + signed<32> tmp_val3 = (signed<8>)rs1_val[31:24] * (signed<8>)rs2_val[31:24] + (signed<8>)rd_val[31:24]; + rd_val = rd_val + tmp_val3 + tmp_val2 + tmp_val1 + tmp_val0; + X[rd] = rd_val; + } + } + } + // SMAQA.SU + // This instruction compares the 8-bit signed integer elements in Rs1 with the 8-bit signed integer elements in Rs2 + // and selects the numbers that is greater than the other one. The selected results are written to Rd. + // Rd.B[x] = (Rs1.B[x] > Rs2.B[x]) ? Rs1.B[x] : Rs2.B[x]; + SMAX8 { + encoding: 7'b1000101 :: rs2[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + unsigned<8> rd_val0 = rs1_val[7:0] > rs2_val[7:0] ? rs1_val[7:0] : rs2_val[7:0]; + unsigned<8> rd_val1 = rs1_val[15:8] > rs2_val[15:8] ? rs1_val[15:8] : rs2_val[15:8]; + unsigned<8> rd_val2 = rs1_val[23:16] > rs2_val[23:16] ? rs1_val[23:16] : rs2_val[23:16]; + unsigned<8> rd_val3 = rs1_val[31:24] > rs2_val[31:24] ? rs1_val[31:24] : rs2_val[31:24]; + unsigned<32> rd_val = rd_val3 :: rd_val2 :: rd_val1 :: rd_val0; + X[rd] = rd_val; + } + } + } + // This instruction compares the 16-bit signed integer elements in Rs1 with the 16-bit signed integer elements in Rs2 + // and selects the numbers that is greater than the other one. The selected results are written to Rd. + // Rd.H[x] = (Rs1.H[x] > Rs2.H[x]) ? Rs1.H[x] : Rs2.H[x]; + SMAX16 { + encoding: 7'b1000001 :: rs2[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + unsigned<16> rd_val0 = rs1_val[15:0] > rs2_val[15:0] ? rs1_val[15:0] : rs2_val[15:0]; + unsigned<16> rd_val1 = rs1_val[31:16] > rs2_val[31:16] ? rs1_val[31:16] : rs2_val[31:16]; + unsigned<32> rd_val = rd_val1 :: rd_val0; + X[rd] = rd_val; + } + } + } + // SMBB16 + // SMBT16 + // SMTT16 + // SMDS + // SMDRS + // SMXDS + // This instruction compares the 8-bit signed integer elements in Rs1 with the 8-bit signed integer elements in Rs2 + // and selects the numbers that is less than the other one. The selected results are written to Rd. + // Rd.B[x] = (Rs1.B[x] < Rs2.B[x]) ? Rs1.B[x] : Rs2.B[x]; + SMIN8 { + encoding: 7'b1000100 :: rs2[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + unsigned<8> rd_val0 = rs1_val[7:0] < rs2_val[7:0] ? rs1_val[7:0] : rs2_val[7:0]; + unsigned<8> rd_val1 = rs1_val[15:8] < rs2_val[15:8] ? rs1_val[15:8] : rs2_val[15:8]; + unsigned<8> rd_val2 = rs1_val[23:16] < rs2_val[23:16] ? rs1_val[23:16] : rs2_val[23:16]; + unsigned<8> rd_val3 = rs1_val[31:24] < rs2_val[31:24] ? rs1_val[31:24] : rs2_val[31:24]; + unsigned<32> rd_val = rd_val3 :: rd_val2 :: rd_val1 :: rd_val0; + X[rd] = rd_val; + } + } + } + // SMIN16 + // SMMUL + // SMMUL.u + // SMMWB + // SMMWB.u + // SMMWT + // SMMWT.u + // SRA.U + // SRAI.U + // SRA8 + // SRA8.u + // SRAI8 + // SRAI8.u + // SRA16 + // SRA16.u + // SRAI16 + // SRAI16.u + // SRL8 + // SRL8.u + // SRLI8 + // SRLI8.u + // SRL16 + // SRL16.u + // SRLI16 + // SRLI16.u + // STAS16 + // STSA16 + // This instruction subtracts the 8-bit integer elements in Rs2 from the 8-bit integer elements in Rs1, + // and then writes the result to Rd. + // Rd.B[x] = Rs1.B[x] - Rs2.B[x]; + SUB8 { + encoding: 7'b0100101 :: rs2[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + signed<8> rd_val0 = (signed<8>)rs1_val[7:0] - (signed<8>)rs2_val[7:0]; + signed<8> rd_val1 = (signed<8>)rs1_val[15:8] - (signed<8>)rs2_val[15:8]; + signed<8> rd_val2 = (signed<8>)rs1_val[23:16] - (signed<8>)rs2_val[23:16]; + signed<8> rd_val3 = (signed<8>)rs1_val[31:24] - (signed<8>)rs2_val[31:24]; + X[rd] = rd_val3 :: rd_val2 :: rd_val1 :: rd_val0; + } + } + } + SUB8QQQ { + encoding: 7'b0100101 :: rs2[4:0] :: rs1[4:0] :: 3'b001 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + X[rd][7:0] = (signed<8>)rs1_val[7:0] - (signed<8>)rs2_val[7:0]; + X[rd][15:8] = (signed<8>)rs1_val[15:8] - (signed<8>)rs2_val[15:8]; + X[rd][23:16] = (signed<8>)rs1_val[23:16] - (signed<8>)rs2_val[23:16]; + X[rd][31:24] = (signed<8>)rs1_val[31:24] - (signed<8>)rs2_val[31:24]; + } + } + } + SUB8WWW { + encoding: 7'b0100101 :: rs2[4:0] :: rs1[4:0] :: 3'b011 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + X[rd][7:0] = (signed<8>)X[rs1][7:0] - (signed<8>)X[rs2][7:0]; + X[rd][15:8] = (signed<8>)X[rs1][15:8] - (signed<8>)X[rs2][15:8]; + X[rd][23:16] = (signed<8>)X[rs1][23:16] - (signed<8>)X[rs2][23:16]; + X[rd][31:24] = (signed<8>)X[rs1][31:24] - (signed<8>)X[rs2][31:24]; + } + } + } + // SUB16 + // TODO (SUNPKD8xy) + // SUNPKD8 { + // encoding: 7'b1010110 :: code[4:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + // assembly:"{name(rs1)}, {name(rd)}"; + // behavior: { + // if(rd != 0) { + // unsigned<32> rs1_val = X[rs1]; + // // TODO: move to helper function + // if(code == 5'b01000) { // SUNPKD810 + // signed<8> rs1_val_hi = rs1_val[15:8]; + // signed<8> rs1_val_lo = rs1_val[7:0]; + // } else if (code == 5'b01001) { // SUNPKD820 + // signed<8> rs1_val_hi = rs1_val[23:16]; + // signed<8> rs1_val_lo = rs1_val[7:0]; + // } else if (code == 5'b01010) { // SUNPKD830 + // signed<8> rs1_val_hi = rs1_val[31:24]; + // signed<8> rs1_val_lo = rs1_val[7:0]; + // } else if (code == 5'b01011) { // SUNPKD831 + // signed<8> rs1_val_hi = rs1_val[31:24]; + // signed<8> rs1_val_lo = rs1_val[15:8]; + // } else if (code == 5'b10011) { // SUNPKD832 + // signed<8> rs1_val_hi = rs1_val[31:24]; + // signed<8> rs1_val_lo = rs1_val[23:16]; + // } else { + // raise(0, 2); // Invalid instruction? + // } + // X[rd] = (signed<16>)rs1_val_hi :: (unsigned<16>)(signed<16>)rs1_val_lo; + // } + // } + // } + SUNPKD810 { + encoding: 7'b1010110 :: 5'b01000 :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + signed<8> rs1_val_hi = rs1_val[15:8]; + signed<8> rs1_val_lo = rs1_val[7:0]; + X[rd] = (signed<16>)rs1_val_hi :: (unsigned<16>)(signed<16>)rs1_val_lo; + } + } + } + SUNPKD820 { + encoding: 7'b1010110 :: 5'b01001 :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + signed<8> rs1_val_hi = rs1_val[23:16]; + signed<8> rs1_val_lo = rs1_val[7:0]; + X[rd] = (signed<16>)rs1_val_hi :: (unsigned<16>)(signed<16>)rs1_val_lo; + } + } + } + SUNPKD830 { + encoding: 7'b1010110 :: 5'b01010 :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + signed<8> rs1_val_hi = rs1_val[31:24]; + signed<8> rs1_val_lo = rs1_val[7:0]; + X[rd] = (signed<16>)rs1_val_hi :: (unsigned<16>)(signed<16>)rs1_val_lo; + } + } + } + SUNPKD831 { + encoding: 7'b1010110 :: 5'b01011 :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + signed<8> rs1_val_hi = rs1_val[31:24]; + signed<8> rs1_val_lo = rs1_val[15:8]; + X[rd] = (signed<16>)rs1_val_hi :: (unsigned<16>)(signed<16>)rs1_val_lo; + } + } + } + SUNPKD832 { + encoding: 7'b1010110 :: 5'b10011 :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + signed<8> rs1_val_hi = rs1_val[31:24]; + signed<8> rs1_val_lo = rs1_val[23:16]; + X[rd] = (signed<16>)rs1_val_hi :: (unsigned<16>)(signed<16>)rs1_val_lo; + } + } + } + // SWAP8 + // SWAP16 + // UCLIP8 + // UCLIP16 + // UCLIP32 + // UCMPLE8 + // UCMPLE16 + // UCMPLT8 + // UCMPLT16 + // UKADD8 + // UKADD16 + // UKADDH + // UKADDW + // UKCRAS16 + // UKCRSA16 + // UKSTAS16 + // UKSTSA16 + // UKSUB8 + // UKSUB16 + // UKSUBH + // UKSUBW + // UMAQA + // UMAX8 + // UMAX16 + // UMIN8 + // UMIN16 + // UMUL16 + // UMULX16 + // URADD8 + // URADD16 + // URADDW + // URCRAS16 + // URCRSA16 + // URSTAS16 + // URSTSA16 + // URSUB8 + // URSUB16 + // URSUBW + // WEXT + // ZUNPKD810 + // ZUNPKD820 + // ZUNPKD830 + // ZUNPKD831 + // ZUNPKD832 + // TODO + // Mresult = ZE33(Rs1) u* ZE33(Rs2); + // R[Rd(4,1).1(0)][31:0] = Mresult[63:32]; + // R[Rd(4,1).0(0)][31:0] = Mresult[31:0]; + } +} + + +InstructionSet RV32Zbpo extends RV32I { + instructions { + PACK { + encoding: 7'b0000100 :: rs2[4:0] :: rs1[4:0] :: 3'b100 :: rd[4:0] :: 7'b0110011; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned rs1_val = X[rs1]; + unsigned rs2_val = X[rs2]; + unsigned<(XLEN/2)> rd_val_lo = rs1_val[(XLEN/2)-1:0]; + unsigned<(XLEN/2)> rd_val_hi = rs2_val[(XLEN/2)-1:0]; + unsigned rd_val = rd_val_hi :: rd_val_lo; + X[rd] = rd_val; + } + } + } + // TODO + PACKU { + encoding: 7'b0100100 :: rs2[4:0] :: rs1[4:0] :: 3'b100 :: rd[4:0] :: 7'b0110011; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + unsigned rs1_val = X[rs1]; + unsigned rs2_val = X[rs2]; + unsigned<(XLEN/2)> rd_val_lo = rs1_val[XLEN-1:XLEN/2]; + unsigned<(XLEN/2)> rd_val_hi = rs2_val[XLEN-1:XLEN/2]; + unsigned rd_val = rd_val_hi :: rd_val_lo; + X[rd] = rd_val; + } + } + } + // TODO + MIN { + encoding: 7'b0000101 :: rs2[4:0] :: rs1[4:0] :: 3'b100 :: rd[4:0] :: 7'b0110011; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + signed<32> rs1_val = X[rs1]; + signed<32> rs2_val = X[rs2]; + signed<32> rd_val = rs1_val < rs2_val ? rs1_val : rs2_val; + X[rd] = rd_val; + } + } + } + MAX { + encoding: 7'b0000101 :: rs2[4:0] :: rs1[4:0] :: 3'b110 :: rd[4:0] :: 7'b0110011; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + if(rd != 0) { + signed<32> rs1_val = X[rs1]; + signed<32> rs2_val = X[rs2]; + signed<32> rd_val = rs1_val >= rs2_val ? rs1_val : rs2_val; + X[rd] = rd_val; + } + } + } + // REV8.H + // CMIX + // REV + // TODO (MAX) + CLZ { + encoding: 7'b0110000 :: 5'b00000 :: rs1[4:0] :: 3'b001 :: rd[4:0] :: 7'b0010011; + assembly:"{name(rd)}, {name(rs1)}"; + behavior: { + if(rd != 0) { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> count = 0; + unsigned int i = 32; + while (i > 0) { + i = i - 1; + if (rs1_val[i] == 1'b0) { + count = count + 1; + } else { + i = 0; // TODO: break; + } + } + X[rd] = count; + } + } + } + // FSR + // FSRI + } +} + +InstructionSet RV32Zpsfoperand extends RV32I { + // architectural_state { + // // register unsigned<64> &P[16] = X[31:0] [[ty=u32]] [[class=GPR32Pair]]; + // // Not allowed in CoreDSL2 (needs arbitrary packed/unpacked casts) + // } + instructions { + // SMAL + // RADD64 + // URADD64 + // KADD64 + // UKADD64 + // RSUB64 + // URSUB64 + // KSUB64 + // UKSUB64 + // TODO + // c64 = r[dU].r[dL]; + // t64 = c64 + rs1 s* rs2; + // r[dU].r[dL] = t64; + SMAR64 { + encoding: 7'b1000010 :: rs2[4:0] :: rs1[4:0] :: 3'b001 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + signed<64> rs1_val = X[rs1]; + signed<64> rs2_val = X[rs2]; + signed<64> rd_hi = X[rd[4:1]*2+1]; // ??? + signed<32> rd_lo = X[rd[4:1]*2]; + signed<64> temp = rd_hi :: rd_lo; + signed<64> temp2 = temp + rs1_val * rs2_val; + // discard overflow + X[rd[4:1]*2+1] = temp2[63:32]; + X[rd[4:1]*2] = temp2[31:0]; + } + } + + // SMSR64 + // UMAR64 + // UMSR64 + // KMAR64 + // KMSR64 + // UKMAR64 + // UKMSR64 + // SMALBB + // SMALBT + // SMALTT + // SMALDA + // SMALXDA + // SMALDS + // SMALDRS + // SMALXDS + // SMSLDA + // SMSLXDA + // TODO + MULR64 { + encoding: 7'b1111000 :: rs2[4:0] :: rs1[4:0] :: 3'b001 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + unsigned<32> rs1_val = X[rs1]; + unsigned<32> rs2_val = X[rs2]; + unsigned<64> product = rs1_val * rs2_val; + // X[rd[4:1]*2+1] = product[63:32]; + // X[rd[4:1]*2] = product[31:0]; + // X[rd[4:1] :: 1'b1 : rd[4:1] :: 1'b0] = product; + // X[odd(rd):even(rd)] = product; + // X[rd[4:1]*2+1:rd[4:1]*2] = product; + if (rd % 2) raise(0, 2); // alternative: rd & 1 + if (rs1 % 2) raise(0, 2); // alternative: rs1 & 1 + if (rs2 % 2) raise(0, 2); // alternative: rs2 & 1 + X[rd+1:rd] = product; + } + } + // MULSR64 + // UMUL8 + // UMULX8 + // UMUL16 + // UMULX16 + // SMUL8 + // SMULX8 + // SMUL16 + // SMULX16 + // TODO + ADD64 { + encoding: 7'b1100000 :: rs2[4:0] :: rs1[4:0] :: 3'b001 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + // signed<64> rs1_val_hi = X[rs1[4:1]*2+1]; // ??? + // unsigned<32> rs1_val_lo = X[rs1[4:1]*2]; + // signed<64> rs1_val = rs1_val_hi :: rs1_val_lo; + // signed<64> rs2_val_hi = X[rs2[4:1]*2+1]; // ??? + // unsigned<32> rs2_val_lo = X[rs2[4:1]*2]; + // signed<64> rs2_val = rs2_val_hi :: rs2_val_lo; + // signed<64> sum = rs1_val + rs2_val; + // X[rd[4:1]*2+1] = sum[63:32]; + // X[rd[4:1]*2] = sum[31:0]; + if (rd % 2) raise(0, 2); // alternative: rd & 1 + X[rd+1:rd] = X[rs1+1:rs1] + X[rs2+1:rs2]; + } + } + // TODO + SUB64 { + encoding: 7'b1100001 :: rs2[4:0] :: rs1[4:0] :: 3'b001 :: rd[4:0] :: 7'b1110111; + assembly:"{name(rs2)}, {name(rs1)}, {name(rd)}"; + behavior: { + signed<64> rs1_val_hi = X[rs1[4:1]*2+1]; // ??? + signed<32> rs1_val_lo = X[rs1[4:1]*2]; + signed<64> rs1_val = rs1_val_hi :: rs1_val_lo; + signed<64> rs2_val_hi = X[rs2[4:1]*2+1]; // ??? + signed<32> rs2_val_lo = X[rs2[4:1]*2]; + signed<64> rs2_val = rs2_val_hi :: rs2_val_lo; + signed<64> sum = rs1_val - rs2_val; + X[rd[4:1]*2+1] = sum[63:32]; + X[rd[4:1]*2] = sum[31:0]; + } + } + } +} + +// InstructionSet RV32P extends RV32Zpn, RV32Zbpo, RV32Zpsfoperand { +// // architectural_state { +// // // unsigned<32> VXSAT_ADDR_ = 0x009; +// // // unsigned<32>& VXSAT_CSR_ = CSR[VXSAT_ADDR_]; +// // } +// } diff --git a/examples/cdsl/rv_tumeda/XCoreVNand.core_desc b/examples/cdsl/rv_tumeda/XCoreVNand.core_desc index f96abbbf..b599aed2 100644 --- a/examples/cdsl/rv_tumeda/XCoreVNand.core_desc +++ b/examples/cdsl/rv_tumeda/XCoreVNand.core_desc @@ -13,7 +13,7 @@ InstructionSet XCoreVNand extends RV32I { // } CV_NAND_BITWISE { encoding: 7'b1001001 :: rs2[4:0] :: rs1[4:0] :: 3'b110 :: rd[4:0] :: 7'b0101011; - assembly: {"cv.nand_bitwise", "{name(rd)}, {name(rs1)}, {name(rs2)}"}; + assembly: {"cv.nand.bitwise", "{name(rd)}, {name(rs1)}, {name(rs2)}"}; behavior: { if(rd != 0) X[rd] = ~(X[rs1] & X[rs2]); } diff --git a/examples/cdsl/rv_xcorev b/examples/cdsl/rv_xcorev index e97bbdee..f66bf9df 160000 --- a/examples/cdsl/rv_xcorev +++ b/examples/cdsl/rv_xcorev @@ -1 +1 @@ -Subproject commit e97bbdee60762b2e1e37d27cc94d819c8faaf9a9 +Subproject commit f66bf9df6b44ce8541ba529649368b3f3fa55040 diff --git a/examples/cfg/filter.yml b/examples/cfg/filter.yml index 42f90d7a..653009a0 100644 --- a/examples/cfg/filter.yml +++ b/examples/cfg/filter.yml @@ -2,11 +2,13 @@ filter: sets: # keep: [XCoreVAlu] - drop: [RISCVBase, RISCVEncoding, Zicsr, Zifencei, RVSMode, RVDebug, RV32I, RVNMode, XCoreVSimd] + # drop: [RISCVBase, RISCVEncoding, Zicsr, Zifencei, RVSMode, RVDebug, RV32I, RVNMode, XCoreVSimd] + drop: [RISCVBase, RISCVEncoding, Zicsr, Zifencei, RVSMode, RVDebug, RV32I, RVNMode] + # drop: [RISCVBase, RISCVEncoding, Zicsr, Zifencei, RVSMode, RVDebug, RV32I, RVNMode, RV32Zpsfoperand] instructions: # keep: [CV_ABS, CV_ADD_B] - drop: [CV_CLIPU, CV_CLIPR, CV_CLIPUR] + drop: [CV_CLIPU, CV_CLIPR, CV_CLIPUR, CV_SLET, CV_SLETU] opcodes: - keep: [custom-0, custom-1, custom-2 ,custom-3, 0b00000] + keep: [custom-0, custom-1, custom-2 ,custom-3, 0b00000, OP-P, OP] encoding_sizes: keep: [32] diff --git a/examples/cfg/patches.yml b/examples/cfg/patches.yml index 9e94882d..66dbd55f 100644 --- a/examples/cfg/patches.yml +++ b/examples/cfg/patches.yml @@ -15,7 +15,9 @@ patches: # file: gitlab_ci.patch # file: llvm/gitlab_ci.patch # file: /absolute/path/to/gitlab_ci.patch - - name: insert_markers_llvm17 + # - name: insert_markers_llvm17 + - name: insert_markers_llvm18 + # TODO: automatially select patch depending on llvm version # generated patch (TODO: implement) # - name: ??? # target: llvm diff --git a/examples/cfg/riscv.yml b/examples/cfg/riscv.yml new file mode 100644 index 00000000..70f7e95c --- /dev/null +++ b/examples/cfg/riscv.yml @@ -0,0 +1,76 @@ +--- +riscv: + xlen: 32 + features: + - m + - fast-unaligned-access + legalization: + gisel: + ops: + # TODO: simd shift? + # TODO: rotate left/right? + # - name: [G_SMAX, G_UMAX, G_SMIN, G_UMIN, G_ABS] + # types: [s32] + # onlyif: [HasVendorXCValu] + # - name: [G_ADD, G_SUB, G_AND, G_OR, G_XOR, G_ASHR, G_LSHR, G_SHL] + # types: [v4i8, v2i16] + # onlyif: [HasVendorXCVsimd] + # - name: G_INSERT_VECTOR_ELT + # types: [v4i8, v2i16] + # onlyif: [HasVendorXCVsimd] + # - name: [G_ADD] + # types: [v4i8, v2i16] + # onlyif: [HasExtmyextsimd] + - name: [G_ADD] + types: [s16, s32] + onlyif: [HasExtmyextalu] + # - name: [G_ADD, G_SMAX] + # types: [v4i8, v2i16] + # onlyif: [HasExtRV32Zpn] + # - name: [G_SHL, G_SMIN, G_SUB] + # types: [v4i8] + # onlyif: [HasExtRV32Zpn] + # - name: [G_SMIN, G_SMAX] # TODO: CLZ, REV, CMIX + # types: [s32] + # onlyif: [HasExtRV32Zbpo] + # # TODO: RV32Zpsfoperand (register pairs) + # TODO: G_BITCAST + transform_info: + shouldFoldTerminatingConditionAfterLSR: true + prefersVectorizedAddressing: false + enableInterleavedAccessVectorization: true + enableMaskedInterleavedAccessVectorization: null + enableScalableVectorization: false + preferEpilogueVectorization: null + supportsScalableVectors: false + # getInliningThresholdMultiplier: ? + # getInliningCostBenefitAnalysisSavingsMultiplier: ? + # getInliningCostBenefitAnalysisProfitableMultiplier: ? + # getInlinerVectorBonusPercent: ? + # getFlatAddressSpace: ? + # getMinVectorRegisterBitWidth: ? + # getCacheLineSize: ? + # getPrefetchDistance: ? + # getMaxPrefetchIterationsAhead: ? + # getMaxMemIntrinsicInlineSizeThreshold: ? + # getAtomicMemIntrinsicMaxElementSize: ? + # getMaxNumArgs: ? + # getGISelRematGlobalCost: ? + # getMinTripCountTailFoldingThreshold: ? + # getMaxVScale: ? + # getVScaleForTuning: ? + # getPredictableBranchThreshold: ? + # isSingleThreaded: ? + # isNumRegsMajorCostOfLSR: ? + # canMacroFuseCmp: ? + # enableOrderedReductions: ? + # LSRWithInstrQueries: ? + # useAA: ? + # shouldBuildLookupTables: ? + # shouldBuildRelLookupTables: ? + # supportsEfficientVectorElementLoadStore: ? + # supportsTailCalls: ? + # enableSelectOptimize: ? + # isFPVectorizationPotentiallyUnsafe: ? + # isVScaleKnownToBeAPowerOfTwo: ? + # enableWritePrefetching: ? diff --git a/examples/cfg/tests.yml b/examples/cfg/tests.yml new file mode 100644 index 00000000..7ce61004 --- /dev/null +++ b/examples/cfg/tests.yml @@ -0,0 +1,5 @@ +--- +test: + paths: [] + # - MC/RISCV + # - CodeGen/RISCV diff --git a/examples/cfg/xcorev/XCoreVAlu.yml b/examples/cfg/xcorev/XCoreVAlu.yml index 53e501fe..3c43f88b 100644 --- a/examples/cfg/xcorev/XCoreVAlu.yml +++ b/examples/cfg/xcorev/XCoreVAlu.yml @@ -1,9 +1,16 @@ --- extensions: XCoreVAlu: - feature: XCVAlu + feature: XCValu arch: xcvalu version: "1.0" experimental: false vendor: true # patches: [] +passes: + per_model: + XCoreVAlu: + skip: [riscv_features, riscv_isa_info, riscv_instr_formats, riscv_instr_info, behav_to_pat] + override: + behav_to_pat: + patterns: false diff --git a/examples/cfg/xcorev/XCoreVBitmanip.yml b/examples/cfg/xcorev/XCoreVBitmanip.yml index 2f9fd7a8..5966e448 100644 --- a/examples/cfg/xcorev/XCoreVBitmanip.yml +++ b/examples/cfg/xcorev/XCoreVBitmanip.yml @@ -1,9 +1,16 @@ --- extensions: XCoreVBitmanip: - feature: XCVBitmanip + feature: XCVbitmanip arch: xcvbitmanip version: "1.0" experimental: false vendor: true # patches: [] +passes: + per_model: + XCoreVBitmanip: + skip: [riscv_features, riscv_isa_info, riscv_instr_formats, riscv_instr_info, behav_to_pat] + override: + behav_to_pat: + patterns: false diff --git a/examples/cfg/xcorev/XCoreVBranchImmediate.yml b/examples/cfg/xcorev/XCoreVBranchImmediate.yml index 4dd7beb3..470327d0 100644 --- a/examples/cfg/xcorev/XCoreVBranchImmediate.yml +++ b/examples/cfg/xcorev/XCoreVBranchImmediate.yml @@ -1,9 +1,16 @@ --- extensions: XCoreVBranchImmediate: - feature: XCVBi + feature: XCVbi arch: xcvbi version: "1.0" experimental: false vendor: true # patches: [] +passes: + per_model: + XCoreVBranchImmediate: + skip: [riscv_features, riscv_isa_info, riscv_instr_formats, riscv_instr_info, behav_to_pat] + override: + behav_to_pat: + patterns: false diff --git a/examples/cfg/xcorev/XCoreVMac.yml b/examples/cfg/xcorev/XCoreVMac.yml index 2cac5a83..ecf0ce37 100644 --- a/examples/cfg/xcorev/XCoreVMac.yml +++ b/examples/cfg/xcorev/XCoreVMac.yml @@ -1,9 +1,16 @@ --- extensions: XCoreVMac: - feature: XCVMac + feature: XCVmac arch: xcvmac version: "1.0" experimental: false vendor: true # patches: [] +passes: + per_model: + XCoreVMac: + skip: [riscv_features, riscv_isa_info, riscv_instr_formats, riscv_instr_info, behav_to_pat] + override: + behav_to_pat: + patterns: false diff --git a/examples/cfg/xcorev/XCoreVMem.yml b/examples/cfg/xcorev/XCoreVMem.yml index fb0a1aa8..b69ac115 100644 --- a/examples/cfg/xcorev/XCoreVMem.yml +++ b/examples/cfg/xcorev/XCoreVMem.yml @@ -1,9 +1,16 @@ --- extensions: XCoreVMem: - feature: XCVMem + feature: XCVmem arch: xcvmem version: "1.0" experimental: false vendor: true # patches: [] +passes: + per_model: + XCoreVMem: + skip: [riscv_features, riscv_isa_info, riscv_instr_formats, riscv_instr_info, behav_to_pat] + override: + behav_to_pat: + patterns: false diff --git a/examples/cfg/xcorev/XCoreVSimd.yml b/examples/cfg/xcorev/XCoreVSimd.yml index eb00b4fb..ea5d0cca 100644 --- a/examples/cfg/xcorev/XCoreVSimd.yml +++ b/examples/cfg/xcorev/XCoreVSimd.yml @@ -1,9 +1,16 @@ --- extensions: XCoreVSimd: - feature: XCVSimd + feature: XCVsimd arch: xcvsimd version: "1.0" experimental: false vendor: true # patches: [] +passes: + per_model: + XCoreVSimd: + skip: [riscv_features, riscv_isa_info, riscv_instr_formats, riscv_instr_info, behav_to_pat] + override: + behav_to_pat: + patterns: false diff --git a/examples/demo.py b/examples/demo.py index 9508d0dc..a5f886d8 100644 --- a/examples/demo.py +++ b/examples/demo.py @@ -34,38 +34,38 @@ # VERBOSE = True # FAST = False FAST = True +SKIP_PATTERNS = False +# SKIP_PATTERNS = True +INTERACTIVE = False seal5_flow = Seal5Flow("/tmp/seal5_llvm_demo", "demo") +# Optional: clean existing settings/models for fresh run +seal5_flow.reset(settings=True, interactive=False) +seal5_flow.clean(temp=True, patches=True, models=True, inputs=True, interactive=INTERACTIVE) + # Clone LLVM and init seal5 metadata directory seal5_flow.initialize( clone=True, clone_url="https://github.com/llvm/llvm-project.git", - clone_ref="llvmorg-17.0.6", + # clone_ref="llvmorg-17.0.6", + clone_ref="llvmorg-18.1.0-rc3", force=True, verbose=VERBOSE, ) -# Optional: clean existing settings/models for fresh run -seal5_flow.reset(settings=True, interactive=False) - -# Clone Seal5 dependencies -# 1. M2-ISA-R (frontend only) -# 2. CDSL2LLVM (later) -# TODO: refresh refs -seal5_flow.setup(force=True, verbose=VERBOSE) - # Load CoreDSL inputs cdsl_files = [ # XCOREV EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVMac.core_desc", EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVAlu.core_desc", - EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVBitmanip.core_desc", - EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVSimd.core_desc", - EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVMem.core_desc", - EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVBranchImmediate.core_desc", + # EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVBitmanip.core_desc", + # EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVSimd.core_desc", + # EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVMem.core_desc", + # EXAMPLES_DIR / "cdsl" / "rv_xcorev" / "XCoreVBranchImmediate.core_desc", # RVP (will not work) + # EXAMPLES_DIR / "cdsl" / "RV32P.core_desc", # EXAMPLES_DIR / "cdsl" / "RVP.core_desc", # S4E (untested) -> undefined XLEN # EXAMPLES_DIR / "cdsl" / "rv_s4e" / "s4e-mac.core_desc", @@ -78,15 +78,26 @@ ] seal5_flow.load(cdsl_files, verbose=VERBOSE, overwrite=True) +# Load test inputs +test_files = [ + # EXAMPLES_DIR / "tests" / "xcorev" / "cv_abs.test.c", + EXAMPLES_DIR / "tests" / "cv_nand" / "cv_nand.c", + EXAMPLES_DIR / "tests" / "cv_nand" / "cv_nand.s", + EXAMPLES_DIR / "tests" / "cv_nand" / "cv_nand_invalid.s", + # TODO: support subdirectories to avoid duplicate test names (WARN!) + # EXAMPLES_DIR / "tests" / "cv_nand" / "*.c", # TODO: support glob patterns +] +seal5_flow.load(test_files, verbose=VERBOSE, overwrite=True) + # Load YAML inputs cfg_files = [ # XCOREV EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVMac.yml", EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVAlu.yml", - EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVBitmanip.yml", - EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVSimd.yml", - EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVMem.yml", - EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVBranchImmediate.yml", + # EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVBitmanip.yml", + # EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVSimd.yml", + # EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVMem.yml", + # EXAMPLES_DIR / "cfg" / "xcorev" / "XCoreVBranchImmediate.yml", # S4E # TUMEDA # GENERATED @@ -94,10 +105,16 @@ EXAMPLES_DIR / "cfg" / "llvm.yml", EXAMPLES_DIR / "cfg" / "filter.yml", EXAMPLES_DIR / "cfg" / "patches.yml", + EXAMPLES_DIR / "cfg" / "riscv.yml", + EXAMPLES_DIR / "cfg" / "tests.yml", EXAMPLES_DIR / "cfg" / "git.yml", ] seal5_flow.load(cfg_files, verbose=VERBOSE, overwrite=False) +# Clone & install Seal5 dependencies +# 1. CDSL2LLVM (add PHASE_0 patches) +seal5_flow.setup(force=True, verbose=VERBOSE) + # Apply initial patches seal5_flow.patch(verbose=VERBOSE, stages=[PatchStage.PHASE_0]) @@ -111,11 +128,25 @@ # 3. Analyse/optimize instructions seal5_flow.transform(verbose=VERBOSE) -# Generate patches -seal5_flow.generate(verbose=VERBOSE) +# Generate patches (except Patterns) +seal5_flow.generate(verbose=VERBOSE, skip=["pattern_gen"]) + +# Apply next patches +seal5_flow.patch(verbose=VERBOSE, stages=[PatchStage.PHASE_1, PatchStage.PHASE_2]) + +if not FAST: + # Build patched LLVM + seal5_flow.build(verbose=VERBOSE, config="release") +if not SKIP_PATTERNS: + # Build PatternGen & llc + seal5_flow.build(verbose=VERBOSE, config="release", target="pattern-gen") + seal5_flow.build(verbose=VERBOSE, config="release", target="llc") + + # Generate remaining patches + seal5_flow.generate(verbose=VERBOSE, only=["pattern_gen"]) -# Apply patches -seal5_flow.patch(verbose=VERBOSE) + # Apply patches + seal5_flow.patch(verbose=VERBOSE) # Build patched LLVM seal5_flow.build(verbose=VERBOSE, config="release") @@ -130,4 +161,4 @@ seal5_flow.export("/tmp/seal5_llvm_demo.tar.gz", verbose=VERBOSE) # Optional: cleanup temorary files, build dirs,... -# seal5.cleanup(temp=True, build=True, deps=True, force=True) +# seal5.clean(temp=True, build=True, deps=True, interactive=INTERACTIVE) diff --git a/examples/tests/cv_nand/cv_nand.c b/examples/tests/cv_nand/cv_nand.c new file mode 100644 index 00000000..60900f01 --- /dev/null +++ b/examples/tests/cv_nand/cv_nand.c @@ -0,0 +1,16 @@ +// RUN: clang -cc1 -triple riscv32 -target-feature +m -target-feature +xcorevnand -mllvm -global-isel=1 -S -O3 %s -o - \ +// RUN: | FileCheck %s -check-prefix=CHECK + +// CHECK-LABEL: nand_bitwise_s32: +// CHECK-COUNT-1: cv.nand.bitwise {{.*}} +signed int nand_bitwise_s32(signed int a, signed int b) +{ + return ~(a & b); +} + +// CHECK-LABEL: nand_bitwise_u32: +// CHECK-COUNT-1: cv.nand.bitwise {{.*}} +unsigned int nand_bitwise_u32(unsigned int a, unsigned int b) +{ + return ~(a & b); +} diff --git a/examples/tests/cv_nand/cv_nand.s b/examples/tests/cv_nand/cv_nand.s new file mode 100644 index 00000000..31eb6854 --- /dev/null +++ b/examples/tests/cv_nand/cv_nand.s @@ -0,0 +1,9 @@ +# RUN: llvm-mc %s -triple=riscv32 -mattr=+xcorevnand -riscv-no-aliases -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ %s +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+xcorevnand < %s \ +# RUN: | llvm-objdump --mattr=+xcorevnand -M no-aliases -d -r - \ +# RUN: | FileCheck --check-prefix=CHECK-ASM-AND-OBJ %s + +# CHECK-ASM-AND-OBJ: cv.nand.bitwise a4, ra, s0 +# CHECK-ASM: encoding: [0x2b,0xe7,0x80,0x92] +cv.nand.bitwise a4, ra, s0 diff --git a/examples/tests/cv_nand/cv_nand_invalid.s b/examples/tests/cv_nand/cv_nand_invalid.s new file mode 100644 index 00000000..2208f50a --- /dev/null +++ b/examples/tests/cv_nand/cv_nand_invalid.s @@ -0,0 +1,3 @@ +# RUN: not llvm-mc -triple riscv32 -mattr=+m < %s 2>&1 | FileCheck %s + +cv.nand.bitwise a4, ra, s0 # CHECK: :[[@LINE]]:1: error: instruction requires the following: 'XCoreVNand' (XCoreVNand Extension) diff --git a/examples/tests/xcorev/cv_abs.test.c b/examples/tests/xcorev/cv_abs.test.c new file mode 100644 index 00000000..0a48c221 --- /dev/null +++ b/examples/tests/xcorev/cv_abs.test.c @@ -0,0 +1,9 @@ +// RUN: %clang --target=riscv32 -march=rv32ixs4emac -c -o %t.o %s +// RUN: llvm-objdump --disassembler-options=numeric -d %t.o | FileCheck %s + +int main() { + // CHECK: 0b 90 1a 01 cv.abs x1, x2 + asm("cv.abs x1, x2"); + + return 0; +} diff --git a/examples/tests/xcorev/cv_mac.test.c b/examples/tests/xcorev/cv_mac.test.c new file mode 100644 index 00000000..0505648e --- /dev/null +++ b/examples/tests/xcorev/cv_mac.test.c @@ -0,0 +1,9 @@ +// RUN: %clang --target=riscv32 -march=rv32ixs4emac -c -o %t.o %s +// RUN: llvm-objdump --disassembler-options=numeric -d %t.o | FileCheck %s + +int main() { + // CHECK: 0b 90 1a 01 cv.mac x1, x2, x3 + asm("cv.mac x1, x2, x3"); + + return 0; +} diff --git a/instr_info_td_notes.txt b/instr_info_td_notes.txt new file mode 100644 index 00000000..bc6aaa0a --- /dev/null +++ b/instr_info_td_notes.txt @@ -0,0 +1,79 @@ +CV_SUBINCACC { // InstructionEncoding Instruction Sched RVInst_CV_SUBINCACC + int Size = 4; + string DecoderNamespace = ""; + list Predicates = [HasExtXExample, IsRV32]; + string DecoderMethod = ""; + bit hasCompleteDecoder = 1; + string Namespace = "RISCV"; + dag OutOperandList = (outs); + dag InOperandList = (ins GPR:$rs1, GPR:$rs2); + string AsmString = "cv_subincacc $rd, $rs1, $rs2"; + EncodingByHwMode EncodingInfos = ?; + list Pattern = ?; + list Uses = []; + list Defs = []; + int CodeSize = 0; + int AddedComplexity = 0; + bit isPreISelOpcode = 0; + bit isReturn = 0; + bit isBranch = 0; + bit isEHScopeReturn = 0; + bit isIndirectBranch = 0; + bit isCompare = 0; + bit isMoveImm = 0; + bit isMoveReg = 0; + bit isBitcast = 0; + bit isSelect = 0; + bit isBarrier = 0; + bit isCall = 0; + bit isAdd = 0; + bit isTrap = 0; + bit canFoldAsLoad = 0; + bit mayLoad = 0; + bit mayStore = 0; + bit mayRaiseFPException = 0; + bit isConvertibleToThreeAddress = 0; + bit isCommutable = 0; + bit isTerminator = 0; + bit isReMaterializable = 0; + bit isPredicable = 0; + bit isUnpredicable = 0; + bit hasDelaySlot = 0; + bit usesCustomInserter = 0; + bit hasPostISelHook = 0; + bit hasCtrlDep = 0; + bit isNotDuplicable = 0; + bit isConvergent = 0; + bit isAuthenticated = 0; + bit isAsCheapAsAMove = 0; + bit hasExtraSrcRegAllocReq = 0; + bit hasExtraDefRegAllocReq = 0; + bit isRegSequence = 0; + bit isPseudo = 0; + bit isMeta = 0; + bit isExtractSubreg = 0; + bit isInsertSubreg = 0; + bit variadicOpsAreDefs = 0; + bit hasSideEffects = 0; + bit isCodeGenOnly = 0; + bit isAsmParserOnly = 0; + bit hasNoSchedulingInfo = 0; + InstrItinClass Itinerary = NoItinerary; + list SchedRW = []; + string Constraints = ""; + string DisableEncoding = ""; + string PostEncoderMethod = ""; + bits<64> TSFlags = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + string AsmMatchConverter = ""; + string TwoOperandAliasConstraint = ""; + string AsmVariantName = ""; + bit UseNamedOperandTable = 0; + bit UseLogicalOperandMappings = 0; + bit FastISelShouldIgnore = 0; + bit HasPositionOrder = 0; + bits<32> SoftFail = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + bits<32> Inst = { 0, 1, 0, 1, 0, 0, 0, rs2{4}, rs2{3}, rs2{2}, rs2{1}, rs2{0}, rs1{4}, rs1{3}, rs1{2}, rs1{1}, rs1{0}, 0, 1, 1, rd{4}, rd{3}, rd{2}, rd{1}, rd{0}, 0, 1, 0, 1, 0, 1, 1 }; + bits<5> rd = { ?, ?, ?, ?, ? }; + bits<5> rs1 = { ?, ?, ?, ?, ? }; + bits<5> rs2 = { ?, ?, ?, ?, ? }; +} diff --git a/seal5/backends/coredsl2/writer.py b/seal5/backends/coredsl2/writer.py index 7397376d..ea0cc5db 100644 --- a/seal5/backends/coredsl2/writer.py +++ b/seal5/backends/coredsl2/writer.py @@ -16,7 +16,7 @@ from typing import Union from . import visitor -from m2isar.metamodel import arch, patch_model, behav +from m2isar.metamodel import arch, patch_model logger = logging.getLogger("coredsl2_writer") diff --git a/seal5/backends/llvmir/writer.py b/seal5/backends/llvmir/writer.py index a1620075..576d138d 100644 --- a/seal5/backends/llvmir/writer.py +++ b/seal5/backends/llvmir/writer.py @@ -116,7 +116,12 @@ def main(): install_dir = pathlib.Path(install_dir) try: cdsl2llvm.run_pattern_gen( - install_dir / "llvm" / "build", input_file, output_file, skip_patterns=True, skip_formats=True + # install_dir / "llvm" / "build", + install_dir, + input_file, + output_file, + skip_patterns=True, + skip_formats=True, ) metrics["n_success"] += 1 except AssertionError: diff --git a/seal5/backends/patterngen/writer.py b/seal5/backends/patterngen/writer.py index acabf7c9..f34eb30b 100644 --- a/seal5/backends/patterngen/writer.py +++ b/seal5/backends/patterngen/writer.py @@ -34,6 +34,7 @@ def main(): parser.add_argument("--output", "-o", type=str, default=None) parser.add_argument("--splitted", action="store_true", help="Split per set and instruction") parser.add_argument("--formats", action="store_true", help="Also generate instruction formats") + parser.add_argument("--patterns", action="store_true", help="Also generate instruction patterns") parser.add_argument("--metrics", default=None, help="Output metrics to file") parser.add_argument("--index", default=None, help="Output index to file") parser.add_argument("--ext", type=str, default="td", help="Default file extension (if using --splitted)") @@ -90,9 +91,20 @@ def main(): # print("model", model) artifacts = {} artifacts[None] = [] # used for global artifacts + settings = model.get("settings", None) if args.splitted: # errs = [] - model_includes = [] + # model_includes = [] + default_mattr = "+m,+fast-unaligned-access" + if settings: + riscv_settings = settings.riscv + if riscv_settings: + features = riscv_settings.features + if features is None: + pass + else: + default_mattr = ",".join([f"+{f}" for f in features]) + assert out_path.is_dir(), "Expecting output directory when using --splitted" for set_name, set_def in model["sets"].items(): artifacts[set_name] = [] @@ -120,24 +132,32 @@ def main(): if not input_file.is_file(): metrics["n_skipped"] += 1 continue + # if args.patterns: out_name = f"{instr_def.name}.{args.ext}" - out_name_fmt = f"{instr_def.name}InstrFormat.{args.ext}" output_file = set_dir / out_name - output_file_fmt = set_dir / out_name_fmt + if args.formats: + out_name_fmt = f"{instr_def.name}InstrFormat.{args.ext}" + output_file_fmt = set_dir / out_name_fmt install_dir = os.getenv("CDSL2LLVM_DIR", None) - ext = None + predicate = None + mattr = default_mattr if ext_settings is not None: - ext = ext_settings.get_predicate(name=set_name) + predicate = ext_settings.get_predicate(name=set_name) + arch_ = ext_settings.get_arch(name=set_name) + mattr = ",".join([*mattr.split(","), f"+{arch_}"]) + assert install_dir is not None install_dir = pathlib.Path(install_dir) try: cdsl2llvm.run_pattern_gen( - install_dir / "llvm" / "build", + # install_dir / "llvm" / "build", + install_dir, input_file, output_file, - skip_patterns=False, + skip_patterns=not args.patterns, skip_formats=not args.formats, - ext=ext, + ext=predicate, + mattr=mattr, ) if output_file.is_file(): metrics["n_success"] += 1 @@ -147,11 +167,12 @@ def main(): artifacts[set_name].append(file_artifact_fmt) include_path_fmt = f"{set_name}/{out_name_fmt}" includes.append(include_path_fmt) - file_artifact_dest = f"llvm/lib/Target/RISCV/seal5/{set_name}/{out_name}" - file_artifact = File(file_artifact_dest, src_path=output_file) - artifacts[set_name].append(file_artifact) - include_path = f"{set_name}/{out_name}" - includes.append(include_path) + if args.patterns: + file_artifact_dest = f"llvm/lib/Target/RISCV/seal5/{set_name}/{out_name}" + file_artifact = File(file_artifact_dest, src_path=output_file) + artifacts[set_name].append(file_artifact) + include_path = f"{set_name}/{out_name}" + includes.append(include_path) else: metrics["n_failed"] += 1 except AssertionError: @@ -160,15 +181,17 @@ def main(): if len(includes) > 0: set_includes_str = "\n".join([f'include "seal5/{inc}"' for inc in includes]) set_includes_artifact_dest = f"llvm/lib/Target/RISCV/seal5/{set_name}.td" - set_includes_artifact = File(set_includes_artifact_dest, content=set_includes_str) + set_name_lower = set_name.lower() + key = f"{set_name_lower}_set_td_includes" + set_includes_artifact = NamedPatch(set_includes_artifact_dest, key=key, content=set_includes_str) artifacts[set_name].append(set_includes_artifact) - model_includes.append(f"{set_name}.td") - if len(model_includes) > 0: - model_includes_str = "\n".join([f'include "seal5/{inc}"' for inc in model_includes]) - model_includes_artifact_dest = "llvm/lib/Target/RISCV/seal5.td" - key = "seal5_td_includes" - model_includes_artifact = NamedPatch(model_includes_artifact_dest, key, content=model_includes_str) - artifacts[None].append(model_includes_artifact) + # model_includes.append(f"{set_name}.td") + # if len(model_includes) > 0: + # model_includes_str = "\n".join([f'include "seal5/{inc}"' for inc in model_includes]) + # model_includes_artifact_dest = "llvm/lib/Target/RISCV/seal5.td" + # key = "seal5_td_includes" + # model_includes_artifact = NamedPatch(model_includes_artifact_dest, key, content=model_includes_str) + # artifacts[None].append(model_includes_artifact) # if len(errs) > 0: # # print("errs", errs) # for insn_name, err_str in errs: diff --git a/seal5/backends/riscv_gisel_legalizer/__init__.py b/seal5/backends/riscv_gisel_legalizer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/seal5/backends/riscv_gisel_legalizer/writer.py b/seal5/backends/riscv_gisel_legalizer/writer.py new file mode 100644 index 00000000..94d60b47 --- /dev/null +++ b/seal5/backends/riscv_gisel_legalizer/writer.py @@ -0,0 +1,172 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# This file is part of the M2-ISA-R project: https://github.com/tum-ei-eda/M2-ISA-R +# +# Copyright (C) 2022 +# Chair of Electrical Design Automation +# Technical University of Munich + +"""Clean M2-ISA-R/Seal5 metamodel to .core_desc file.""" + +import argparse +import logging +import pathlib + +from seal5.index import NamedPatch, write_index_yaml +from seal5.settings import RISCVLegalizerSettings, Seal5Settings + +logger = logging.getLogger("riscv_gisel_legalizer") + +# if (ST.hasVendorXCvsimd()) { + + +def type_helper(ty): + print("type_helper", ty) + ty_ = ty.replace("seal5_", "") + if ty_.startswith("p"): + sz = ty_[1:] + sz = int(sz) + return f"const LLT {ty} = LLT::pointer({sz}, XLen);" + elif ty_.startswith("s"): + sz = ty_[1:] + sz = int(sz) + return f"const LLT {ty} = LLT::scalar({sz});" + elif ty_.startswith("v"): + n, sz = ty_[1:].split("i", 1) + n = int(n) + sz = int(sz) + return f"const LLT {ty} = LLT::fixed_vector({n}, LLT::scalar({sz}));" + else: + raise RuntimeError(f"Unsupported: {ty}") + + +def gen_riscv_gisel_legalizer_str(legalizer_settings: RISCVLegalizerSettings): + print("legalizer_settings", legalizer_settings) + ops = legalizer_settings.ops + used_types = [] + types_lines = [] + settings_lines = [] + for op in ops: + print("op", op) + names = op.name + print("names", names) + types = op.types + print("types", types) + onlyif = op.onlyif + print("onlyif", onlyif) + if not isinstance(names, list): + assert isinstance(names, str) + names = [names] + print("names'", names) + if not isinstance(types, list): + assert isinstance(types, str) + names = [names] + print("types'", types) + types = [f"seal5_{ty}" for ty in types] + types_str = "{" + ", ".join(types) + "}" + for ty in types: + if ty not in used_types: + line = type_helper(ty) + types_lines.append(line) + used_types.append(ty) + names_str = "{" + ", ".join(names) + "}" + line = "" + if onlyif: + cond = " && ".join(["ST." + pred[0].lower() + pred[1:] + "()" for pred in onlyif]) + line += f"if ({cond}) " + line += f"getActionDefinitionsBuilder({names_str}).legalFor({types_str});" + settings_lines.append(line) + + print("used_types") + ret = "" + ret += "{\n" + ret += "\n".join(types_lines) + ret += "\n" + ret += "\n".join(settings_lines) + ret += "\n}" + return ret + + +def main(): + """Main app entrypoint.""" + + # read command line args + parser = argparse.ArgumentParser() + parser.add_argument("top_level", help="A .m2isarmodel or .seal5model file.") + parser.add_argument("--log", default="info", choices=["critical", "error", "warning", "info", "debug"]) + parser.add_argument("--output", "-o", type=str, default=None) + parser.add_argument("--metrics", default=None, help="Output metrics to file") + parser.add_argument("--index", default=None, help="Output index to file") + parser.add_argument("--ext", type=str, default="td", help="Default file extension (if using --splitted)") + parser.add_argument("--yaml", type=str, default=None) + args = parser.parse_args() + + # initialize logging + logging.basicConfig(level=getattr(logging, args.log.upper())) + + # resolve model paths + # top_level = pathlib.Path(args.top_level) + # abs_top_level = top_level.resolve() + + # is_seal5_model = False + # # print("top_level", top_level) + # # print("suffix", top_level.suffix) + # if top_level.suffix == ".seal5model": + # is_seal5_model = True + # if args.output is None: + # assert top_level.suffix in [".m2isarmodel", ".seal5model"], "Can not infer model type from file extension." + # raise NotImplementedError + + # # out_path = top_level.parent / (top_level.stem + ".core_desc") + # else: + assert args.output is not None + out_path = pathlib.Path(args.output) + + assert args.yaml is not None + assert pathlib.Path(args.yaml).is_file() + settings = Seal5Settings.from_yaml_file(args.yaml) + + # logger.info("loading models") + # if not is_seal5_model: + # raise NotImplementedError + + # load models + + artifacts = {} + artifacts[None] = [] # used for global artifacts + if settings: + riscv_settings = settings.riscv + if riscv_settings: + legalization_settings = riscv_settings.legalization + if legalization_settings: + gisel_settings = legalization_settings.get("gisel", None) + if gisel_settings: + content = gen_riscv_gisel_legalizer_str(gisel_settings) + with open(out_path, "w") as f: + f.write(content) + riscv_gisel_legalizer_patch = NamedPatch( + "llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp", + key="riscv_legalizer_info", + src_path=out_path, + ) + artifacts[None].append(riscv_gisel_legalizer_patch) + if args.metrics: + raise NotImplementedError + # metrics_file = args.metrics + # with open(metrics_file, "w") as f: + # f.write(",".join(metrics.keys())) + # f.write("\n") + # f.write(",".join(map(str, metrics.values()))) + # f.write("\n") + if args.index: + if sum(map(lambda x: len(x), artifacts.values())) > 0: + global_artifacts = artifacts.get(None, []) + set_artifacts = {key: value for key, value in artifacts.items() if key is not None} + index_file = args.index + write_index_yaml(index_file, global_artifacts, set_artifacts, content=True) + else: + logger.warning("No patches generated. No index file will be written.") + + +if __name__ == "__main__": + main() diff --git a/seal5/backends/riscv_instr_info/__init__.py b/seal5/backends/riscv_instr_info/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/seal5/backends/riscv_instr_info/templates/__init__.py b/seal5/backends/riscv_instr_info/templates/__init__.py new file mode 100644 index 00000000..8a979b22 --- /dev/null +++ b/seal5/backends/riscv_instr_info/templates/__init__.py @@ -0,0 +1,3 @@ +import pathlib + +template_dir = pathlib.Path(__file__).parent.resolve() diff --git a/seal5/backends/riscv_instr_info/templates/instr_tablegen.mako b/seal5/backends/riscv_instr_info/templates/instr_tablegen.mako new file mode 100644 index 00000000..d924d724 --- /dev/null +++ b/seal5/backends/riscv_instr_info/templates/instr_tablegen.mako @@ -0,0 +1,48 @@ +def ${name} : Instruction, Sched<${sched_str}> { + // General + let Namespace = "RISCV"; + let Size = ${xlen // 8}; + bits<32> SoftFail = 0; + bits<${xlen}> Inst; + + // Operands + % for operand in operands: + bits<${operand.length}> ${operand.name}; + % endfor + + // Attributes + % for key, value in attrs.items(): + let ${key} = ${value}; + % endfor + + // Encoding + % for enc in fields: + + % if enc.length == 1: + % if enc.extra is None: + let Inst{${enc.start}} = ${enc.name}; + % else: + let Inst{${enc.start}} = ${bin(enc.extra)};${" // " + enc.name if enc.name else ""} + % endif + % else: + % if enc.extra is None: + let Inst{${enc.start+enc.length-1}-${enc.start}} = ${enc.name}; + % else: + let Inst{${enc.start+enc.length-1}-${enc.start}} = ${f"0b{enc.extra:0{enc.length}b}"};${" // " + enc.name if enc.name else ""} + % endif + % endif + % endfor + + + // Operands + dag InOperandList = (ins ${ins_str}); + dag OutOperandList = (outs ${outs_str}); + + // Assembly + let AsmString = "${real_name}\t${asm_str}"; + + % if len(constraints_str): + // Constraints + let Constraints = "${constraints_str}"; + % endif +} diff --git a/seal5/backends/riscv_instr_info/templates/instr_tablegen2.mako b/seal5/backends/riscv_instr_info/templates/instr_tablegen2.mako new file mode 100644 index 00000000..b06ac797 --- /dev/null +++ b/seal5/backends/riscv_instr_info/templates/instr_tablegen2.mako @@ -0,0 +1,48 @@ +// class RVInst_${name} : RVInst { +class RVInst_${name} : Instruction, Sched<${sched_str}> { + // General + let Namespace = "RISCV"; + let Size = ${xlen // 8}; + bits<32> SoftFail = 0; + bits<${xlen}> Inst; + + // Operands + % for operand in operands: + bits<${operand.length}> ${operand.name}; + % endfor + + // Attributes + % for key, value in attrs.items(): + let ${key} = ${value}; + % endfor + + // Encoding + % for enc in fields: + + % if enc.length == 1: + % if enc.extra is None: + let Inst{${enc.start}} = ${enc.name}; + % else: + let Inst{${enc.start}} = ${bin(enc.extra)};${" // " + enc.name if enc.name else ""} + % endif + % else: + % if enc.extra is None: + let Inst{${enc.start+enc.length-1}-${enc.start}} = ${enc.name}; + % else: + let Inst{${enc.start+enc.length-1}-${enc.start}} = ${f"0b{enc.extra:0{enc.length}b}"};${" // " + enc.name if enc.name else ""} + % endif + % endif + % endfor + + dag OutOperandList = outs; + dag InOperandList = ins; + + // Assembly + let AsmString = "${real_name}\t${asm_str}"; + + % if len(constraints_str): + // Constraints + let Constraints = "${constraints_str}"; + % endif +} +def ${name} : RVInst_${name}<(outs ${outs_str}), (ins ${ins_str})>; diff --git a/seal5/backends/riscv_instr_info/writer.py b/seal5/backends/riscv_instr_info/writer.py new file mode 100644 index 00000000..7f7df48c --- /dev/null +++ b/seal5/backends/riscv_instr_info/writer.py @@ -0,0 +1,401 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# This file is part of the M2-ISA-R project: https://github.com/tum-ei-eda/M2-ISA-R +# +# Copyright (C) 2022 +# Chair of Electrical Design Automation +# Technical University of Munich + +"""Clean M2-ISA-R/Seal5 metamodel to .core_desc file.""" + +import re +import argparse +import logging +import pathlib +import pickle +from typing import Union + +from mako.template import Template + +from m2isar.metamodel import arch + +from seal5.index import NamedPatch, File, write_index_yaml + +# from seal5.settings import ExtensionsSettings +from seal5.model import Seal5OperandAttribute, Seal5InstrAttribute + +from .templates import template_dir + +logger = logging.getLogger("riscv_instr_info") + + +# MAKO_TEMPLATE = """def Feature${predicate} : SubtargetFeature<"${arch}", "Has${predicate}", "true", "'${feature}' (${description})">; +# +# def Has${predicate} : Predicate<"Subtarget->has${predicate}()">, AssemblerPredicate<(any_of Feature${predicate}), "'${feature}' (${description})">;""" + + +class Operand: + def __init__(self, name, lower, upper): + self.name = name + # self._name = name + self.lower = lower + self.upper = upper + + @property + def length(self): + return (self.upper - self.lower + 1) if self.upper >= self.lower else (self.lower - self.upper + 1) + + # @property + # def name(self): + # ty, name_ = self._name.split(":") + # if self.lower == 1: + # ty += "_lsb0" + # ret = f"{ty}:{name_}" + # return ret + + def __repr__(self): + return f"Operand({self.name}, {self.lower}, {self.upper})" + + +class EncodingField: + def __init__(self, name, start, length, value=None, extra=None): + self.name = name + self.start = start + self.length = length + + self.value = value + self.const = value is not None + self.extra = extra if extra is not None else value + + def __repr__(self): + return f"EncodingField({self.name}, {self.start}, {self.length}, {self.value}, {self.const})" + + +def process_encoding(enc): + # print("get_encoding", enc) + operands = {} + for e in reversed(enc): + if isinstance(e, arch.BitField): + name = e.name + if name in operands: + op = operands[name] + op.lower = min(e.range.lower, op.lower) + op.upper = max(e.range.upper, op.upper) + else: + op = Operand(name, e.range.lower, e.range.upper) + operands[name] = op + fields = [] + start = 0 + for e in reversed(enc): + if isinstance(e, arch.BitField): + name = e.name + assert name in operands + op = operands[name] + if op.length > e.range.length: + if e.range.length == 1: + name = name + "{" + str(e.range.lower - op.lower) + "}" + else: + name = name + "{" + str(e.range.upper - op.lower) + "-" + str(e.range.lower - op.lower) + "}" + new = EncodingField(name, start, e.range.length) + start += e.range.length + elif isinstance(e, arch.BitVal): + # lower = 0 + new = EncodingField(None, start, e.length, e.value) + start += e.length + else: + assert False + fields.insert(0, new) + # print("fields", fields) + # input(">") + return operands.values(), fields + + +def write_riscv_instruction_info( + name, + real_name, + asm_str, + ins_str, + outs_str, + enc, + fields, + operands, + details_str, + attrs={}, + constraints=[], + formats=False, +): + if formats: + instr_template = Template(filename=str(template_dir / "instr_tablegen2.mako")) + else: + instr_template = Template(filename=str(template_dir / "instr_tablegen.mako")) + + operands, fields = process_encoding(enc) + + attrs = {key: int(value) if isinstance(value, bool) else value for key, value in attrs.items()} + constraints_str = ", ".join(constraints) + + out_str = instr_template.render( + name=name, + real_name=real_name, + xlen=32, + asm_str=asm_str, + ins_str=ins_str, + outs_str=outs_str, + sched_str=str([]), + operands=operands, + fields=fields, + attrs=attrs, + constraints_str=constraints_str, + ) + + if len(details_str) > 0: + out_str = ( + details_str + + " in {\n" + + "\n".join([(" " + line) if len(line) > 0 else line for line in out_str.split("\n")]) + + "\n} // " + + details_str + + "\n" + ) + + logger.info("writing TableGen for instruction %s", name) + + return out_str + + +def gen_riscv_instr_info_str(instr): + print("instr", instr) + name = instr.name + operands = instr.operands + print("operands", operands) + reads = [] + writes = [] + for op_name, op in operands.items(): + print("op", op) + print("op.constraints", op.constraints) + if len(op.constraints) > 0: + raise NotImplementedError + print("op.attributes", op.attributes) + if Seal5OperandAttribute.IS_REG in op.attributes: + assert Seal5OperandAttribute.REG_CLASS in op.attributes + cls = op.attributes[Seal5OperandAttribute.REG_CLASS] + assert cls in ["GPR"] + pre = cls + elif Seal5OperandAttribute.IS_IMM in op.attributes: + assert Seal5OperandAttribute.TY in op.attributes + ty = op.attributes[Seal5OperandAttribute.TY] + assert ty[0] in ["u", "s"] + sz = int(ty[1:]) + pre = f"{ty}imm{sz}" + + if Seal5OperandAttribute.INOUT in op.attributes or ( + Seal5OperandAttribute.OUT in op.attributes and Seal5OperandAttribute.IN in op.attributes + ): + op_str2 = f"{pre}:${op_name}_wb" + writes.append(op_str2) + op_str = f"{pre}:${op_name}" + reads.append(op_str) + elif Seal5OperandAttribute.OUT in op.attributes: + op_str = f"{pre}:${op_name}" + writes.append(op_str) + elif Seal5OperandAttribute.IN in op.attributes: + op_str = f"{pre}:${op_name}" + reads.append(op_str) + print("reads", reads) + print("writes", writes) + attributes = instr.attributes + print("attributes", attributes) + real_name = instr.mnemonic + asm_str = instr.assembly + asm_str = re.sub( + r"{([a-zA-Z0-9]+)}", + r"$\g<1>", + re.sub(r"{([a-zA-Z0-9]+):[#0-9a-zA-Z\.]+}", r"{\g<1>}", re.sub(r"name\(([a-zA-Z0-9]+)\)", r"\g<1>", asm_str)), + ) + print("asm_str_orig", asm_str) + asm_order = re.compile(r"(\$[a-zA-Z0-9]+)").findall(asm_str) + print("asm_order", asm_order) + for op in asm_order: + if f"{op}(" in asm_str or f"{op})" in asm_str or f"{op}!" in asm_str or f"!{op}" in asm_str: + asm_str = asm_str.replace(op, "${" + op[1:] + "}") + print("asm_str_new", asm_str) + reads_ = [(x.split(":", 1)[1] if ":" in x else x) for x in reads] + print("reads_", reads_) + writes_ = [(x.split(":", 1)[1] if ":" in x else x).replace("_wb", "") for x in writes] + print("writes_", writes_) + + ins_str = ", ".join([reads[reads_.index(x)] for x in asm_order if x in reads_]) + print("ins_str", ins_str) + outs_str = ", ".join([writes[writes_.index(x)] for x in asm_order if x in writes_]) + print("outs_str", outs_str) + details_str = "" + fields = instr.fields + print("fields") + encoding = instr.encoding + print("encoding") + # input(">") + attrs = {} + if Seal5InstrAttribute.HAS_SIDE_EFFECTS in attributes: + attrs["hasSideEffects"] = 1 + else: + attrs["hasSideEffects"] = 0 + if Seal5InstrAttribute.MAY_LOAD in attributes: + attrs["mayLoad"] = 1 + else: + attrs["mayLoad"] = 0 + if Seal5InstrAttribute.MAY_STORE in attributes: + attrs["mayStore"] = 1 + else: + attrs["mayStore"] = 0 + if Seal5InstrAttribute.IS_TERMINATOR in attributes: + attrs["isTerminator"] = 1 + else: + attrs["isTerminator"] = 0 + constraints = instr.constraints + if len(constraints) > 0: + raise NotImplementedError + formats = True + tablegen_str = write_riscv_instruction_info( + name, + real_name, + asm_str, + ins_str, + outs_str, + encoding, + fields, + operands, + details_str, + attrs=attrs, + constraints=constraints, + formats=formats, + ) + return tablegen_str + + +def main(): + """Main app entrypoint.""" + + # read command line args + parser = argparse.ArgumentParser() + parser.add_argument("top_level", help="A .m2isarmodel or .seal5model file.") + parser.add_argument("--log", default="info", choices=["critical", "error", "warning", "info", "debug"]) + parser.add_argument("--output", "-o", type=str, default=None) + parser.add_argument("--splitted", action="store_true", help="Split per set") + parser.add_argument("--formats", action="store_true", help="Also generate instruction formats") + parser.add_argument("--metrics", default=None, help="Output metrics to file") + parser.add_argument("--index", default=None, help="Output index to file") + parser.add_argument("--ext", type=str, default="td", help="Default file extension (if using --splitted)") + args = parser.parse_args() + + # initialize logging + logging.basicConfig(level=getattr(logging, args.log.upper())) + + # resolve model paths + top_level = pathlib.Path(args.top_level) + # abs_top_level = top_level.resolve() + + is_seal5_model = False + # print("top_level", top_level) + # print("suffix", top_level.suffix) + if top_level.suffix == ".seal5model": + is_seal5_model = True + if args.output is None: + assert top_level.suffix in [".m2isarmodel", ".seal5model"], "Can not infer model type from file extension." + raise NotImplementedError + + # out_path = top_level.parent / (top_level.stem + ".core_desc") + else: + out_path = pathlib.Path(args.output) + + logger.info("loading models") + if not is_seal5_model: + raise NotImplementedError + + # load models + with open(top_level, "rb") as f: + # models: "dict[str, arch.CoreDef]" = pickle.load(f) + if is_seal5_model: + model: "dict[str, Union[arch.InstructionSet, ...]]" = pickle.load(f) + model["cores"] = {} + else: # TODO: core vs. set! + temp: "dict[str, Union[arch.InstructionSet, arch.CoreDef]]" = pickle.load(f) + assert len(temp) > 0, "Empty model!" + if isinstance(list(temp.values())[0], arch.CoreDef): + model = {"cores": temp, "sets": {}} + elif isinstance(list(temp.values())[0], arch.InstructionSet): + model = {"sets": temp, "cores": {}} + else: + assert False + + metrics = { + "n_sets": 0, + "n_skipped": 0, + "n_failed": 0, + "n_success": 0, + } + # preprocess model + # print("model", model) + # settings = model.get("settings", None) + artifacts = {} + artifacts[None] = [] # used for global artifacts + if args.splitted: + content = "" + # errs = [] + for set_name, set_def in model["sets"].items(): + set_name_lower = set_name.lower() + artifacts[set_name] = [] + includes = [] + set_dir = out_path / set_name + set_dir.mkdir(exist_ok=True) + ext_settings = set_def.settings + pred = None + if ext_settings is not None: + pred = "Has" + ext_settings.get_predicate(name=set_name) + metrics["n_sets"] += 1 + for instr_name, instr_def in set_def.instructions.items(): + metrics["n_success"] += 1 + out_name = f"{instr_def.name}InstrInfo.{args.ext}" + output_file = set_dir / out_name + content = gen_riscv_instr_info_str(instr_def) + if len(content) > 0: + assert pred is not None + predicate_str = f"Predicates = [{pred}, IsRV32]" + content = f"let {predicate_str} in {{\n{content}\n}}" + with open(output_file, "w") as f: + f.write(content) + instr_info_patch = File( + f"llvm/lib/Target/RISCV/seal5/{set_name}/{output_file.name}", + src_path=output_file, + ) + artifacts[set_name].append(instr_info_patch) + inc = f"seal5/{set_name}/{output_file.name}" + includes.append(inc) + + includes_str = "\n".join([f'include "{inc}"' for inc in includes]) + set_td_includes_patch = NamedPatch( + f"llvm/lib/Target/RISCV/seal5/{set_name}.td", + key=f"{set_name_lower}_set_td_includes", + content=includes_str, + ) + artifacts[set_name].append(set_td_includes_patch) + else: + raise NotImplementedError + if args.metrics: + metrics_file = args.metrics + with open(metrics_file, "w") as f: + f.write(",".join(metrics.keys())) + f.write("\n") + f.write(",".join(map(str, metrics.values()))) + f.write("\n") + if args.index: + if sum(map(lambda x: len(x), artifacts.values())) > 0: + global_artifacts = artifacts.get(None, []) + set_artifacts = {key: value for key, value in artifacts.items() if key is not None} + index_file = args.index + write_index_yaml(index_file, global_artifacts, set_artifacts, content=True) + else: + logger.warning("No patches generated. No index file will be written.") + + +if __name__ == "__main__": + main() diff --git a/seal5/backends/riscv_isa_info/writer.py b/seal5/backends/riscv_isa_info/writer.py index 9bc0a0cd..676b2adc 100644 --- a/seal5/backends/riscv_isa_info/writer.py +++ b/seal5/backends/riscv_isa_info/writer.py @@ -19,17 +19,18 @@ from m2isar.metamodel import arch from seal5.index import NamedPatch, write_index_yaml -from seal5.settings import ExtensionsSettings +from seal5.settings import ExtensionsSettings, LLVMVersion logger = logging.getLogger("riscv_isa_info") -MAKO_TEMPLATE = " {\"${arch}\", RISCVExtensionVersion{${version_major}, ${version_minor}}}," +MAKO_TEMPLATE = ' {"${arch}", RISCVExtensionVersion{${version_major}, ${version_minor}}},' +MAKO_TEMPLATE_LLVM18 = ' {"${arch}", {${version_major}, ${version_minor}}},' -def gen_riscv_isa_info_str(name: str, ext_settings: ExtensionsSettings): - print("name", name) - print("ext_settings", ext_settings) +def gen_riscv_isa_info_str(name: str, ext_settings: ExtensionsSettings, llvm_version: LLVMVersion): + # print("name", name) + # print("ext_settings", ext_settings) arch = ext_settings.get_arch(name=name) version = ext_settings.get_version() if not isinstance(version, str): @@ -37,7 +38,9 @@ def gen_riscv_isa_info_str(name: str, ext_settings: ExtensionsSettings): version = str(float(version)) version_major, version_minor = str(version).split(".", 1) - content_template = Template(MAKO_TEMPLATE) + llvm_major = llvm_version.major + template = MAKO_TEMPLATE_LLVM18 if llvm_major >= 18 else MAKO_TEMPLATE + content_template = Template(template) content_text = content_template.render(arch=arch, version_major=version_major, version_minor=version_minor) # content_text = content_text.rstrip("\n") return content_text @@ -113,6 +116,14 @@ def main(): else: content = "" # errs = [] + settings = model.get("settings", None) + llvm_version = None + if settings: + llvm_settings = settings.llvm + if llvm_settings: + llvm_state = llvm_settings.state + if llvm_state: + llvm_version = llvm_state.version for set_name, set_def in model["sets"].items(): artifacts[set_name] = [] metrics["n_sets"] += 1 @@ -121,7 +132,7 @@ def main(): metrics["n_skipped"] += 1 continue metrics["n_success"] += 1 - content += gen_riscv_isa_info_str(set_name, ext_settings) + content += gen_riscv_isa_info_str(set_name, ext_settings=ext_settings, llvm_version=llvm_version) if len(content) > 0: with open(out_path, "w") as f: f.write(content) @@ -129,9 +140,7 @@ def main(): key = "riscv_isa_info_experimental" else: key = "riscv_isa_info" - riscv_isa_info_patch = NamedPatch( - "llvm/lib/Support/RISCVISAInfo.cpp", key=key, src_path=out_path - ) + riscv_isa_info_patch = NamedPatch("llvm/lib/Support/RISCVISAInfo.cpp", key=key, src_path=out_path) artifacts[None].append(riscv_isa_info_patch) if args.metrics: metrics_file = args.metrics diff --git a/seal5/backends/viewer_seal5/treegen.py b/seal5/backends/viewer_seal5/treegen.py index aaac0746..6800cfd4 100644 --- a/seal5/backends/viewer_seal5/treegen.py +++ b/seal5/backends/viewer_seal5/treegen.py @@ -8,9 +8,9 @@ """Generate a ttk.Treeview representation of a M2-ISA-R model structure.""" -import tkinter as tk +# import tkinter as tk -from ...metamodel import arch, behav +from ...metamodel import behav from .utils import TreeGenContext # pylint: disable=unused-argument diff --git a/seal5/backends/viewer_seal5/utils.py b/seal5/backends/viewer_seal5/utils.py index c30731d5..4de68f63 100644 --- a/seal5/backends/viewer_seal5/utils.py +++ b/seal5/backends/viewer_seal5/utils.py @@ -9,23 +9,13 @@ """Utility stuff for M2-ISA-R viewer""" from typing import TYPE_CHECKING -from anytree import Node, RenderTree +from anytree import Node if TYPE_CHECKING: import tkinter as tk from tkinter import ttk -class TreeGenContext: - """Data keeping class for recursive TreeView generation""" - - def push(self, new_id): - self.parent_stack.append(new_id) - - def pop(self): - return self.parent_stack.pop() - - class TreeGenContext: def __init__(self, parent=None) -> None: if parent: diff --git a/seal5/backends/viewer_seal5/viewer.py b/seal5/backends/viewer_seal5/viewer.py index 9b116b58..be702693 100644 --- a/seal5/backends/viewer_seal5/viewer.py +++ b/seal5/backends/viewer_seal5/viewer.py @@ -20,7 +20,6 @@ from m2isar.backends.viewer_seal5.utils import TkTreeGenContext, TextTreeGenContext from ...metamodel import arch, patch_model -from ...metamodel.utils.expr_preprocessor import process_attributes, process_functions, process_instructions from . import treegen logger = logging.getLogger("viewer") @@ -105,7 +104,8 @@ def main(): if args.text: set_node = Node("Set", parent=sets_node, value=set_name) # add constants to tree - consts_node = Node("Constants", parent=set_node) + # consts_node = Node("Constants", parent=set_node) + # TODO else: set_id = tree.insert("", tk.END, text=set_name) @@ -304,7 +304,7 @@ def main(): if args.text: print("============================") - text = "" + # text = "" for pre, fill, node in RenderTree(root): suffix = "" if hasattr(node, "value"): diff --git a/seal5/backends/yaml/writer.py b/seal5/backends/yaml/writer.py index 11ed59bb..0d4e20d3 100644 --- a/seal5/backends/yaml/writer.py +++ b/seal5/backends/yaml/writer.py @@ -16,7 +16,7 @@ import yaml -from m2isar.metamodel import arch, patch_model, behav +from m2isar.metamodel import arch logger = logging.getLogger("yaml_writer") diff --git a/seal5/dependencies.py b/seal5/dependencies.py index 5c1ecec6..557cd0f3 100644 --- a/seal5/dependencies.py +++ b/seal5/dependencies.py @@ -66,7 +66,7 @@ def __init__(self, clone_url="https://github.com/PhilippvK/M2-ISA-R-private.git" class CDSL2LLVMDependency(GitDependency): # def __init__(self, clone_url="https://github.com/mathis-s/CoreDSL2LLVM.git", ref="main"): - def __init__(self, clone_url="https://github.com/PhilippvK/CoreDSL2LLVM.git", ref="philippvk3"): + def __init__(self, clone_url="https://github.com/PhilippvK/CoreDSL2LLVM.git", ref="philippvk3-llvm18-ops"): super().__init__("cdsl2llvm", clone_url, ref=ref) diff --git a/seal5/flow.py b/seal5/flow.py index b8c68f4f..0fa1e51f 100644 --- a/seal5/flow.py +++ b/seal5/flow.py @@ -27,13 +27,14 @@ from seal5.logging import get_logger, set_log_file, set_log_level from seal5.types import Seal5State, PatchStage -from seal5.settings import Seal5Settings, PatchSettings, DEFAULT_SETTINGS, LLVMConfig +from seal5.settings import Seal5Settings, PatchSettings, DEFAULT_SETTINGS, LLVMConfig, LLVMVersion from seal5.dependencies import cdsl2llvm_dependency from seal5 import utils from seal5.tools import llvm, cdsl2llvm, inject_patches -from seal5.resources.resources import get_patches -from seal5.index import File, NamedPatch, write_index_yaml +from seal5.resources.resources import get_patches, get_test_cfg +from seal5.passes import Seal5Pass, PassType, PassScope, PassManager, filter_passes +import seal5.pass_list as passes logger = get_logger() @@ -74,70 +75,136 @@ def create_seal5_directories(path: Path, directories: list): (path / directory).mkdir(parents=True, exist_ok=True) +def add_test_cfg(tests_dir: Path): + dest = tests_dir / "lit.cfg.py" + logger.debug("Creating test cfg %s", dest) + src = get_test_cfg() + utils.copy(src, dest) + + class Seal5Flow: def __init__(self, directory: Optional[Path] = None, name: str = "default"): self.directory: Path = handle_directory(directory) self.name: str = name self.state: Seal5State = Seal5State.UNKNOWN + self.passes: List[Seal5Pass] = [] # TODO: implement PassManager self.check() - self.settings: Seal5Settings = None - if self.settings_file.is_file(): - self.settings = Seal5Settings.from_yaml_file(self.settings_file) - if self.logs_dir.is_dir(): - pass - set_log_file(self.log_file_path) + self.settings = Seal5Settings.from_dict(DEFAULT_SETTINGS) + # self.settings: Seal5Settings = Seal5Settings(directory=self.directory) + self.settings.directory = str(self.directory) + if self.settings.settings_file.is_file(): + self.settings = Seal5Settings.from_yaml_file(self.settings.settings_file) + if self.settings.logs_dir.is_dir(): + set_log_file(self.settings.log_file_path) if self.settings: set_log_level( console_level=self.settings.logging.console.level, file_level=self.settings.logging.file.level ) - - @property - def meta_dir(self): - return self.directory / ".seal5" - - @property - def settings_file(self): - return self.meta_dir / "settings.yml" - - @property - def deps_dir(self): - return self.meta_dir / "deps" - - @property - def build_dir(self): - return self.meta_dir / "build" - - @property - def install_dir(self): - return self.meta_dir / "install" - - @property - def logs_dir(self): - return self.meta_dir / "logs" - - @property - def models_dir(self): - return self.meta_dir / "models" - - @property - def inputs_dir(self): - return self.meta_dir / "inputs" - - @property - def temp_dir(self): - return self.meta_dir / "temp" - - @property - def gen_dir(self): - return self.meta_dir / "gen" - - @property - def patches_dir(self): # TODO: maybe merge with gen_dir - return self.meta_dir / "patches" - - @property - def log_file_path(self): - return self.logs_dir / "seal5.log" + self.reset_passes() + self.create_passes() + # self.init_global_model() + + # def init_global_model(self, name: str = "Seal5"): + # dest = self.inputs_dir / f"{name}.seal5model" + # args = [ + # "-", + # "-o", + # dest, + # ] + # utils.python( + # "-m", + # "seal5.frontends.dummy.writer", + # *args, + # env=env, + # print_func=logger.info if verbose else logger.debug, + # live=True, + # ) + + # self.update_global_model(name=name) + + # def update_global_model(self, name: str = "Seal5"): + # self.process_settings(name) + + def reset_passes(self): + self.passes = [] + + def add_pass(self, pass_: Seal5Pass): + pass_names = [p.name for p in self.passes] + assert pass_.name not in pass_names, f"Duplicate pass name: {pass_.name}" + self.passes.append(pass_) + + def add_passes(self, pass_list: List[Seal5Pass]): + for pass_ in pass_list: + self.add_pass(pass_) + + def create_passes(self): + # Transforms + TRANSFORM_PASS_MAP = [ + # TODO: Global -> Model + ("convert_models", passes.convert_models, {}), + ("filter_models", passes.filter_model, {}), + ("drop_unused", passes.drop_unused, {}), + ("eliminate_rd_cmp_zero", passes.eliminate_rd_cmp_zero, {}), + ("eliminate_mod_rfs", passes.eliminate_mod_rfs, {}), + ("drop_unused2", passes.drop_unused, {}), + ("optimize_model", passes.optimize_model, {}), + ("infer_types", passes.infer_types, {}), + ("simplify_trivial_slices", passes.simplify_trivial_slices, {}), + ("explicit_truncations", passes.explicit_truncations, {}), + ("process_settings", passes.process_settings, {}), + ("write_yaml", passes.write_yaml, {}), + ("detect_behavior_constraints", passes.detect_behavior_constraints, {}), + ("collect_register_operands", passes.collect_register_operands, {}), + ("collect_immediate_operands", passes.collect_immediate_operands, {}), + ("collect_operand_types", passes.collect_operand_types, {}), + ("detect_side_effects", passes.detect_side_effects, {}), + ("detect_inouts", passes.detect_inouts, {}), + ("detect_registers", passes.detect_registers, {}), + ("write_cdsl_full", passes.write_cdsl, {"split": False, "compat": False}) + # TODO: determine static constraints (xlen,...) -> subtargetvmap + # detect memory adressing modes + # self.detect_adressing_modes(verbose) # TODO + # detect legal GMIR ops (and map to selectiondag?) + # self.detect_legal_ops(verbose=verbose) # TODO + # extract costs/heuristics + # self.extract_costs_and_heuristics(verbose) # TODO + # ("split_models", passes.split_models, {"by_set": False, "by_instr": False}), + ] + for pass_name, pass_handler, pass_options in TRANSFORM_PASS_MAP: + pass_scope = PassScope.MODEL + self.add_pass(Seal5Pass(pass_name, PassType.TRANSFORM, pass_scope, pass_handler, options=pass_options)) + + # Generates + GENERATE_PASS_MAP = [ + ("seal5_td", passes.gen_seal5_td, {}), + # ("model_td", passes.gen_model_td, {}), + ("set_td", passes.gen_set_td, {}), + ("riscv_features", passes.gen_riscv_features_patch, {}), + ("riscv_isa_infos", passes.gen_riscv_isa_info_patch, {}), + # ("riscv_instr_formats", passes.gen_riscv_instr_formats_patch, {}), + ("riscv_instr_info", passes.gen_riscv_instr_info_patch, {}), + # subtarget_tests + # register_types + # operand_types + # instruction_formats + # instruction_infos + # disassembler + # mc_tests + # selection_dag_legalizer + # scalar_costs + # simd_costs + # isel_patterns + # codegen_test + ("riscv_gisel_legalizer", passes.gen_riscv_gisel_legalizer_patch, {}), + # TODO: nested pass lists? + ("pattern_gen", passes.pattern_gen_pass, {}), + ] + for pass_name, pass_handler, pass_options in GENERATE_PASS_MAP: + if pass_name in ["seal5_td", "riscv_gisel_legalizer"]: + pass_scope = PassScope.GLOBAL + else: + pass_scope = PassScope.MODEL + self.add_pass(Seal5Pass(pass_name, PassType.GENERATE, pass_scope, pass_handler, options=pass_options)) def check(self): pass @@ -157,25 +224,31 @@ def initialize( if clone is False and not utils.ask_user("Clone LLVM repository?", default=False, interactive=interactive): logger.error("Target directory does not exist! Aborting...") sys.exit(1) - sha = llvm.clone_llvm_repo(self.directory, clone_url, ref=clone_ref, label=self.name) + sha, version_info = llvm.clone_llvm_repo(self.directory, clone_url, ref=clone_ref, label=self.name) else: if force: - sha = llvm.clone_llvm_repo(self.directory, clone_url, ref=clone_ref, refresh=True, label=self.name) - if self.meta_dir.is_dir(): + sha, version_info = llvm.clone_llvm_repo( + self.directory, clone_url, ref=clone_ref, refresh=True, label=self.name + ) + if self.settings.meta_dir.is_dir(): if force is False and not utils.ask_user( "Overwrite existing .seal5 diretcory?", default=False, interactive=interactive ): - logger.error(f"Directory {self.meta_dir} already exists! Aborting...") + logger.error(f"Directory {self.settings.meta_dir} already exists! Aborting...") sys.exit(1) - self.meta_dir.mkdir(exist_ok=True) + self.settings.meta_dir.mkdir(exist_ok=True) create_seal5_directories( - self.meta_dir, ["deps", "models", "logs", "build", "install", "temp", "inputs", "gen", "patches"] + self.settings.meta_dir, + ["deps", "models", "logs", "build", "install", "temp", "inputs", "gen", "patches", "tests"], ) - self.settings = Seal5Settings.from_dict(DEFAULT_SETTINGS) + add_test_cfg(self.settings.tests_dir) + if version_info: + llvm_version = LLVMVersion(**version_info) + self.settings.llvm.state.version = llvm_version if sha: self.settings.llvm.state.base_commit = sha - self.settings.to_yaml_file(self.settings_file) - set_log_file(self.log_file_path) + self.settings.save() + set_log_file(self.settings.log_file_path) set_log_level(console_level=self.settings.logging.console.level, file_level=self.settings.logging.file.level) logger.info("Completed initialization of Seal5") @@ -186,50 +259,76 @@ def setup( verbose: bool = False, ): logger.info("Installing Seal5 dependencies") - # m2isar_dependency.clone(self.deps_dir / "M2-ISA-R", overwrite=force) logger.info("Cloning CDSL2LLVM") - # cdsl2llvm_dependency.clone(self.deps_dir / "cdsl2llvm", overwrite=force, depth=1) - cdsl2llvm_dependency.clone(self.deps_dir / "cdsl2llvm", overwrite=force) - logger.info("Building PatternGen") - llvm_config = LLVMConfig( - options={ - "CMAKE_BUILD_TYPE": "Release", - "LLVM_BUILD_TOOLS": False, - "LLVM_ENABLE_ASSERTIONS": False, - "LLVM_OPTIMIZED_TABLEGEN": True, - "LLVM_ENABLE_PROJECT": [], - "LLVM_TARGETS_TO_BUILD": ["RISCV"], - } - ) - # llvm_config = LLVMConfig(options={"CMAKE_BUILD_TYPE": "Debug", "LLVM_BUILD_TOOLS": False, "LLVM_ENABLE_ASSERTIONS": False, "LLVM_OPTIMIZED_TABLEGEN": True, "LLVM_ENABLE_PROJECT": [], "LLVM_TARGETS_TO_BUILD": ["RISCV"]}) - cmake_options = llvm_config.options - cdsl2llvm.build_pattern_gen( - self.deps_dir / "cdsl2llvm", - self.deps_dir / "cdsl2llvm" / "llvm" / "build", - cmake_options=cmake_options, - use_ninja=True, - ) - logger.info("Completed build of PatternGen") - logger.info("Building llc") - cdsl2llvm.build_llc( - self.deps_dir / "cdsl2llvm", - self.deps_dir / "cdsl2llvm" / "llvm" / "build", - cmake_options=cmake_options, - use_ninja=True, - ) - logger.info("Completed build of llc") + # cdsl2llvm_dependency.clone(self.settings.deps_dir / "cdsl2llvm", overwrite=force, depth=1) + cdsl2llvm_dependency.clone(self.settings.deps_dir / "cdsl2llvm", overwrite=force) + integrated_pattern_gen = self.settings.tools.pattern_gen.integrated + if integrated_pattern_gen: + logger.info("Adding PatternGen to target LLVM") + patch_settings = cdsl2llvm.get_pattern_gen_patches( + self.settings.deps_dir / "cdsl2llvm", + self.settings.temp_dir, + ) + self.settings.add_patch(patch_settings) + else: + logger.info("Building PatternGen") + llvm_config = LLVMConfig( + options={ + "CMAKE_BUILD_TYPE": "Release", + "LLVM_BUILD_TOOLS": False, + "LLVM_ENABLE_ASSERTIONS": False, + "LLVM_OPTIMIZED_TABLEGEN": True, + "LLVM_ENABLE_PROJECT": [], + "LLVM_TARGETS_TO_BUILD": ["RISCV"], + } + ) + cmake_options = llvm_config.options + cdsl2llvm.build_pattern_gen( + self.settings.deps_dir / "cdsl2llvm", + self.settings.deps_dir / "cdsl2llvm" / "llvm" / "build", + cmake_options=cmake_options, + use_ninja=True, + ) + logger.info("Completed build of PatternGen") + logger.info("Building llc") + cdsl2llvm.build_llc( + self.settings.deps_dir / "cdsl2llvm", + self.settings.deps_dir / "cdsl2llvm" / "llvm" / "build", + cmake_options=cmake_options, + use_ninja=True, + ) + logger.info("Completed build of llc") # input("qqqqqq") logger.info("Completed installation of Seal5 dependencies") def load_cfg(self, file: Path, overwrite: bool = False): + assert file.is_file(), f"File does not exist: {file}" new_settings: Seal5Settings = Seal5Settings.from_yaml_file(file) self.settings.merge(new_settings, overwrite=overwrite) - self.settings.to_yaml_file(self.settings_file) + self.settings.save() + + def load_test(self, file: Path, overwrite: bool = True): + assert file.is_file(), f"File does not exist: {file}" + filename: str = file.name + dest = self.settings.tests_dir / filename + if dest.is_file() and not overwrite: + raise RuntimeError(f"File {filename} already loaded!") + utils.copy(file, dest) + if str(dest) not in self.settings.test.paths: + self.settings.test.paths.append(str(dest)) + self.settings.save() def prepare_environment(self): env = os.environ.copy() - # env["PYTHONPATH"] = str(self.deps_dir / "M2-ISA-R") - env["CDSL2LLVM_DIR"] = str(self.deps_dir / "cdsl2llvm") + # env["PYTHONPATH"] = str(self.settings.deps_dir / "M2-ISA-R") + cdsl2llvm_build_dir = None + integrated_pattern_gen = self.settings.tools.pattern_gen.integrated + if integrated_pattern_gen: + config = "release" # TODO: fetch default config + cdsl2llvm_build_dir = str(self.settings.build_dir / config) + else: + cdsl2llvm_build_dir = str(self.settings.deps_dir / "cdsl2llvm" / "llvm" / "build") + env["CDSL2LLVM_DIR"] = cdsl2llvm_build_dir return env def parse_coredsl(self, file, out_dir, verbose: bool = False): @@ -248,18 +347,18 @@ def parse_coredsl(self, file, out_dir, verbose: bool = False): ) def load_cdsl(self, file: Path, verbose: bool = False, overwrite: bool = False): - assert file.is_file(), "TODO" + assert file.is_file(), f"File does not exist: {file}" filename: str = file.name - dest = self.inputs_dir / filename + dest = self.settings.inputs_dir / filename if dest.is_file() and not overwrite: raise RuntimeError(f"File {filename} already loaded!") # Add file to inputs directory and settings utils.copy(file, dest) self.settings.inputs.append(filename) # Parse CoreDSL file with M2-ISA-R (TODO: Standalone) - dest = self.models_dir + dest = self.settings.models_dir self.parse_coredsl(file, dest, verbose=verbose) - self.settings.to_yaml_file(self.settings_file) + self.settings.save() def load(self, files: List[Path], verbose: bool = False, overwrite: bool = False): logger.info("Loading Seal5 inputs") @@ -270,1040 +369,62 @@ def load(self, files: List[Path], verbose: bool = False, overwrite: bool = False self.load_cfg(file, overwrite=overwrite) elif ext.lower() in [".core_desc"]: self.load_cdsl(file, verbose=verbose, overwrite=overwrite) + elif ext.lower() in [".ll", ".c", ".cc", ".cpp", ".s", ".mir", ".gmir"]: + self.load_test(file, overwrite=overwrite) else: raise RuntimeError(f"Unsupported input type: {ext}") # TODO: only allow single instr set for now and track inputs in settings logger.info("Completed load of Seal5 inputs") - def build(self, config="release", verbose: bool = False): - logger.info("Building Seal5 LLVM") + def build(self, config="release", target="all", verbose: bool = False): + logger.info("Building Seal5 LLVM (%s)", target) llvm_config = self.settings.llvm.configs.get(config, None) assert llvm_config is not None, f"Invalid llvm config: {config}" cmake_options = llvm_config.options - llvm.build_llvm(self.directory, self.build_dir / config, cmake_options=cmake_options) - logger.info("Completed build of Seal5 LLVM") - - def convert_models(self, verbose: bool = False, inplace: bool = False): - assert not inplace - input_files = list(self.models_dir.glob("*.m2isarmodel")) - assert len(input_files) > 0, "No input models found!" - for input_file in input_files: - name = input_file.name - base = input_file.stem - new_name = f"{base}.seal5model" - logger.info("Converting %s -> %s", name, new_name) - args = [ - self.models_dir / name, - "-o", - self.models_dir / new_name, - "--log", - "info", - # "debug", - ] - utils.python( - "-m", - "seal5.transform.converter", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def optimize_model(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Optimizing %s", name) - args = [ - self.models_dir / name, - "--log", - "info", - # "debug", - ] - utils.python( - "-m", - "seal5.transform.optimize_instructions.optimizer", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def infer_types(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Infering types for %s", name) - args = [ - self.models_dir / name, - "--log", - "info", - # "debug", - ] - utils.python( - "-m", - "seal5.transform.infer_types.transform", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def simplify_trivial_slices(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Simplifying trivial slices for %s", name) - args = [ - self.models_dir / name, - "--log", - "info", - # "debug", - ] - utils.python( - "-m", - "seal5.transform.simplify_trivial_slices.transform", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def explicit_truncations(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Adding excplicit truncations for %s", name) - args = [ - self.models_dir / name, - "--log", - "info", - # "debug", - ] - utils.python( - "-m", - "seal5.transform.explicit_truncations.transform", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def process_settings(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Processing settings for %s", name) - args = [ - self.models_dir / name, - "--log", - "info", - # "debug", - "--yaml", - self.settings_file, - ] - utils.python( - "-m", - "seal5.transform.process_settings.transform", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def filter_model(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Filtering %s", name) - filter_settings = self.settings.filter - filter_args = [] - - def get_filter_args(settings, suffix): - ret = [] - keep = list(map(str, settings.keep)) - drop = list(map(str, settings.drop)) - if keep: - ret += [f"--keep-{suffix}", ",".join(keep)] - if drop: - ret += [f"--drop-{suffix}", ",".join(drop)] - return ret - - filter_sets = filter_settings.sets - filter_instructions = filter_settings.instructions - filter_aliases = filter_settings.aliases - filter_intrinsics = filter_settings.intrinsics - filter_opcodes = filter_settings.opcodes - filter_encoding_sizes = filter_settings.encoding_sizes - # TODO: filter_functions - filter_args.extend(get_filter_args(filter_sets, "sets")) - filter_args.extend(get_filter_args(filter_instructions, "instructions")) - filter_args.extend(get_filter_args(filter_aliases, "aliases")) - filter_args.extend(get_filter_args(filter_intrinsics, "intrinsics")) - filter_args.extend(get_filter_args(filter_opcodes, "opcodes")) - filter_args.extend(get_filter_args(filter_encoding_sizes, "encoding-sizes")) - args = [ - self.models_dir / name, - *filter_args, - "--log", - # "info", - "debug", - ] - utils.python( - "-m", - "seal5.transform.filter_model.filter", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def drop_unused(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Dropping unused for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - ] - utils.python( - "-m", - "seal5.transform.drop_unused.optimizer", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def detect_registers(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Detecting registers for %s", name) - args = [ - self.models_dir / name, - "--log", - "info", - # "debug", - ] - utils.python( - "-m", - "seal5.transform.detect_registers", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def detect_behavior_constraints(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Collecting constraints for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - ] - utils.python( - "-m", - "seal5.transform.collect_raises.collect", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def detect_side_effects(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Detecting side effects for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - ] - utils.python( - "-m", - "seal5.transform.detect_side_effects.collect", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def detect_inouts(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Detecting inouts for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - ] - utils.python( - "-m", - "seal5.transform.detect_inouts.collect", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def collect_operand_types(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - # print("input_file", input_file) - # input(">") - name = input_file.name - logger.info("Collecting operand types for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - "--skip-failing", - ] - utils.python( - "-m", - "seal5.transform.collect_operand_types.collect", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - # input("<") - - def collect_register_operands(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Collecting register operands for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - ] - utils.python( - "-m", - "seal5.transform.collect_register_operands.collect", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def collect_immediate_operands(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Collecting immediate operands for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - ] - utils.python( - "-m", - "seal5.transform.collect_immediate_operands.collect", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def eliminate_rd_cmp_zero(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Eliminating rd == 0 for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - ] - utils.python( - "-m", - "seal5.transform.eliminate_rd_cmp_zero.transform", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def eliminate_mod_rfs(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - logger.info("Eliminating op MOD RFS for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - ] - utils.python( - "-m", - "seal5.transform.eliminate_mod_rfs.transform", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def write_yaml(self, verbose: bool = False, inplace: bool = True): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - new_name = name.replace(".seal5model", ".yml") - logger.info("Writing YAML for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - "--output", - self.temp_dir / new_name, - ] - utils.python( - "-m", - "seal5.backends.yaml.writer", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - print(self.temp_dir / new_name) - self.load_cfg(self.temp_dir / new_name, overwrite=False) - - def write_cdsl(self, verbose: bool = False, inplace: bool = True, split: bool = False, compat: bool = False): - assert inplace - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - for input_file in input_files: - name = input_file.name - if split: - new_name = name.replace(".seal5model", "") - else: - new_name = name.replace(".seal5model", ".core_desc") - logger.info("Writing CDSL for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - "--output", - self.temp_dir / new_name, - ] - if split: - (self.temp_dir / new_name).mkdir(exist_ok=True) - args.append("--splitted") - if compat: - args.append("--compat") - utils.python( - "-m", - "seal5.backends.coredsl2.writer", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - # args_compat = [ - # self.models_dir / name, - # "--log", - # # "info", - # "debug", - # "--output", - # self.temp_dir / f"{new_name}_compat", - # "--compat", - # ] - # if split: - # args.append("--splitted") - # utils.python( - # "-m", - # "seal5.backends.coredsl2.writer", - # *args_compat, - # env=self.prepare_environment(), - # print_func=logger.info if verbose else logger.debug, - # live=True, - # ) - - # def write_cdsl_splitted(self, verbose: bool = False, inplace: bool = True): - # assert inplace - # # input_files = list(self.models_dir.glob("*.seal5model")) - # # assert len(input_files) > 0, "No Seal5 models found!" - # # for input_file in input_files: - # # name = input_file.name - # # sub = name.replace(".seal5model", "") - # for _ in [None]: - # set_names = list(self.settings.extensions.keys()) - # # print("set_names", set_names) - # assert len(set_names) > 0, "No sets found" - # for set_name in set_names: - # insn_names = self.settings.extensions[set_name].instructions - # if insn_names is None: - # logger.warning("Skipping empty set %s", set_name) - # continue - # sub = self.settings.extensions[set_name].model - # # TODO: populate model in yaml backend! - # if sub is None: # Fallback - # sub = set_name - # input_file = self.models_dir / f"{sub}.seal5model" - # assert input_file.is_file(), f"File not found: {input_file}" - # assert len(insn_names) > 0, f"No instructions found in set: {set_name}" - # # insn_names = self.collect_instr_names() - # (self.temp_dir / sub / set_name).mkdir(exist_ok=True, parents=True) - # for insn_name in insn_names: - # logger.info("Writing Metamodel for %s/%s/%s", sub, set_name, insn_name) - # args = [ - # input_file, - # "--keep-instructions", - # insn_name, - # "--log", - # "debug", - # # "info", - # "--output", - # self.temp_dir / sub / set_name / f"{insn_name}.seal5model", - # ] - # utils.python( - # "-m", - # "seal5.transform.filter_model.filter", - # *args, - # env=self.prepare_environment(), - # print_func=logger.info if verbose else logger.debug, - # live=True, - # ) - # logger.info("Writing CDSL for %s/%s", sub, insn_name) - # args = [ - # self.temp_dir / sub / set_name / f"{insn_name}.seal5model", - # "--log", - # "debug", - # # "info", - # "--output", - # self.temp_dir / sub / set_name / f"{insn_name}.core_desc" - # ] - # utils.python( - # "-m", - # "seal5.backends.coredsl2.writer", - # *args, - # env=self.prepare_environment(), - # print_func=logger.info if verbose else logger.debug, - # live=True, - # ) - # args_compat = [ - # self.temp_dir / sub / set_name / f"{insn_name}.seal5model", - # "--log", - # # "info", - # "debug", - # "--output", - # self.temp_dir / sub / set_name / f"{insn_name}.core_desc_compat", - # "--compat", - # ] - # utils.python( - # "-m", - # "seal5.backends.coredsl2.writer", - # *args_compat, - # env=self.prepare_environment(), - # print_func=logger.info if verbose else logger.debug, - # live=True, - # ) - - def convert_behav_to_llvmir(self, verbose: bool = False, inplace: bool = True): - assert inplace - # input_files = list(self.temp_dir.glob("*.core_desc_compat")) - # TODO: use different file ext for non-compat? - input_files = list(self.temp_dir.glob("*.core_desc")) - assert len(input_files) > 0, "No files found!" - errs = [] - for input_file in input_files: - name = input_file.name - # new_name = name.replace(".core_desc_compat", ".ll") - # new_name = name.replace(".core_desc_compat", ".td") - logger.info("Writing LLVM-IR for %s", name) - try: - cdsl2llvm.run_pattern_gen( - self.deps_dir / "cdsl2llvm" / "llvm" / "build", - input_file, - None, - skip_patterns=True, - skip_formats=True, - ) - except AssertionError: - pass - # errs.append((str(input_file), str(ex))) - if len(errs) > 0: - print("errs", errs) - - def convert_behav_to_llvmir_splitted(self, verbose: bool = False, split: bool = True): - assert split, "TODO" - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - gen_metrics_file = True - for input_file in input_files: - name = input_file.name - if split: - new_name = name.replace(".seal5model", "") - else: - new_name = name.replace(".seal5model", ".ll") - logger.info("Writing LLVM-IR for %s", name) - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - "--output", - self.temp_dir / new_name, - ] - if split: - (self.temp_dir / new_name).mkdir(exist_ok=True) - args.append("--splitted") - if gen_metrics_file: - # TODO: move to .seal5/metrics - metrics_file = self.temp_dir / (new_name + "_llvmir_metrics.csv") - args.extend(["--metrics", metrics_file]) - utils.python( - "-m", - "seal5.backends.llvmir.writer", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - - def convert_behav_to_tablegen(self, verbose: bool = False, split: bool = True): - assert split, "TODO" - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - formats = True - gen_metrics_file = True - gen_index_file = True - for input_file in input_files: - name = input_file.name - if split: - new_name = name.replace(".seal5model", "") - else: - new_name = name.replace(".seal5model", ".td") - logger.info("Writing TableGen patterns for %s", name) - # TODO: move to patches dir! - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - "--output", - self.temp_dir / new_name, - ] - if split: - (self.temp_dir / new_name).mkdir(exist_ok=True) - args.append("--splitted") - if formats: - args.append("--formats") - if gen_metrics_file: - metrics_file = self.temp_dir / (new_name + "_tblgen_patterns_metrics.csv") - args.extend(["--metrics", metrics_file]) - if gen_index_file: - index_file = self.temp_dir / (new_name + "_tblgen_patterns_index.yml") - args.extend(["--index", index_file]) - utils.python( - "-m", - "seal5.backends.patterngen.writer", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - if gen_index_file: - if index_file.is_file(): - patch_name = f"tblgen_patterns_{input_file.stem}" - patch_settings = PatchSettings( - name=patch_name, - stage=int(PatchStage.PHASE_2), - comment=f"CDSL2LLVM Generated Tablegen Patterns for {input_file.name}", - index=str(index_file), - generated=True, - target="llvm", - ) - self.settings.patches.append(patch_settings) - self.settings.to_yaml_file(self.settings_file) - else: - logger.warning("No patches found!") - - def gen_riscv_features_patch(self, verbose: bool = False, split: bool = False): - assert not split, "TODO" - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - formats = True - gen_metrics_file = True - gen_index_file = True - for input_file in input_files: - name = input_file.name - new_name = name.replace(".seal5model", "") - logger.info("Writing RISCVFeatures.td patch for %s", name) - out_dir = self.patches_dir / new_name - out_dir.mkdir(exist_ok=True) - - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - "--output", - out_dir / "riscv_features.patch", - ] - if split: - args.append("--splitted") - if gen_metrics_file: - metrics_file = out_dir / ("riscv_features_metrics.csv") - args.extend(["--metrics", metrics_file]) - if gen_index_file: - index_file = out_dir / ("riscv_features_index.yml") - args.extend(["--index", index_file]) - utils.python( - "-m", - "seal5.backends.riscv_features.writer", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - if gen_index_file: - if index_file.is_file(): - patch_name = f"riscv_features_{input_file.stem}" - patch_settings = PatchSettings( - name=patch_name, - stage=int(PatchStage.PHASE_1), - comment=f"Generated RISCVFeatures.td patch for {input_file.name}", - index=str(index_file), - generated=True, - target="llvm", - ) - self.settings.patches.append(patch_settings) - self.settings.to_yaml_file(self.settings_file) - else: - logger.warning("No patches found!") - - def gen_riscv_isa_info_patch(self, verbose: bool = False, split: bool = False): - assert not split, "TODO" - input_files = list(self.models_dir.glob("*.seal5model")) - assert len(input_files) > 0, "No Seal5 models found!" - formats = True - gen_metrics_file = True - gen_index_file = True - for input_file in input_files: - name = input_file.name - new_name = name.replace(".seal5model", "") - logger.info("Writing RISCVISAInfo.cpp patch for %s", name) - out_dir = self.patches_dir / new_name - out_dir.mkdir(exist_ok=True) - - args = [ - self.models_dir / name, - "--log", - # "info", - "debug", - "--output", - out_dir / "riscv_isa_info.patch", - ] - if split: - args.append("--splitted") - if gen_metrics_file: - metrics_file = out_dir / ("riscv_isa_info_metrics.csv") - args.extend(["--metrics", metrics_file]) - if gen_index_file: - index_file = out_dir / ("riscv_isa_info_index.yml") - args.extend(["--index", index_file]) - utils.python( - "-m", - "seal5.backends.riscv_isa_info.writer", - *args, - env=self.prepare_environment(), - print_func=logger.info if verbose else logger.debug, - live=True, - ) - if gen_index_file: - if index_file.is_file(): - patch_name = f"riscv_isa_info_{input_file.stem}" - patch_settings = PatchSettings( - name=patch_name, - stage=int(PatchStage.PHASE_1), - comment=f"Generated RISCVISAInfo.cpp patch for {input_file.name}", - index=str(index_file), - generated=True, - target="llvm", - ) - self.settings.patches.append(patch_settings) - self.settings.to_yaml_file(self.settings_file) - else: - logger.warning("No patches found!") - - # def convert_behav_to_tablegen_splitted(self, verbose: bool = False, inplace: bool = True): - # assert inplace - # # input_files = list(self.models_dir.glob("*.seal5model")) - # # assert len(input_files) > 0, "No Seal5 models found!" - # errs = [] - # # for input_file in input_files: - # # name = input_file.name - # # sub = name.replace(".seal5model", "") - # for _ in [None]: - # set_names = list(self.settings.extensions.keys()) - # assert len(set_names) > 0, "No sets found" - # for set_name in set_names: - # insn_names = self.settings.extensions[set_name].instructions - # if insn_names is None: - # logger.warning("Skipping empty set %s", set_name) - # continue - # assert len(insn_names) > 0, f"No instructions found in set: {set_name}" - # sub = self.settings.extensions[set_name].model - # # TODO: populate model in yaml backend! - # if sub is None: # Fallbacke - # sub = set_name - # for insn_name in insn_names: - # ll_file = self.temp_dir / sub / set_name / f"{insn_name}.ll" - # if not ll_file.is_file(): - # logger.warning("Skipping %s due to errors.", insn_name) - # continue - # # input_file = self.temp_dir / sub / set_name / f"{insn_name}.core_desc_compat" - # input_file = self.temp_dir / sub / set_name / f"{insn_name}.core_desc" - # assert input_file.is_file(), f"File not found: {input_file}" - # output_file = input_file.parent / (input_file.stem + ".td") - # name = input_file.name - # logger.info("Writing TableGen for %s", name) - # ext = self.settings.extensions[set_name].feature - # if ext is None: # fallback to set_name - # ext = set_name.replace("_", "") - # try: - # cdsl2llvm.run_pattern_gen(self.deps_dir / "cdsl2llvm" / "llvm" / "build", input_file, output_file, skip_patterns=False, skip_formats=False, ext=ext) - # except AssertionError: - # pass - # # errs.append((insn_name, str(ex))) - # if len(errs) > 0: - # # print("errs", errs) - # for insn_name, err_str in errs: - # print("Err:", insn_name, err_str) - # input("!") - - def convert_llvmir_to_gmir_splitted(self, verbose: bool = False, inplace: bool = True): - assert inplace - # input_files = list(self.models_dir.glob("*.seal5model")) - # assert len(input_files) > 0, "No Seal5 models found!" - errs = [] - # for input_file in input_files: - # name = input_file.name - # sub = name.replace(".seal5model", "") - for _ in [None]: - set_names = list(self.settings.extensions.keys()) - assert len(set_names) > 0, "No sets found" - for set_name in set_names: - insn_names = self.settings.extensions[set_name].instructions - if insn_names is None: - logger.warning("Skipping empty set %s", set_name) - continue - assert len(insn_names) > 0, f"No instructions found in set: {set_name}" - sub = self.settings.extensions[set_name].model - # TODO: populate model in yaml backend! - if sub is None: # Fallbacke - sub = set_name - for insn_name in insn_names: - ll_file = self.temp_dir / sub / set_name / f"{insn_name}.ll" - if not ll_file.is_file(): - logger.warning("Skipping %s due to errors.", insn_name) - continue - output_file = ll_file.parent / (ll_file.stem + ".gmir") - name = ll_file.name - logger.info("Writing gmir for %s", name) - try: - cdsl2llvm.convert_ll_to_gmir( - self.deps_dir / "cdsl2llvm" / "llvm" / "build", ll_file, output_file - ) - except AssertionError: - pass - # errs.append((insn_name, str(ex))) - if len(errs) > 0: - # print("errs", errs) - for insn_name, err_str in errs: - print("Err:", insn_name, err_str) - input("!") - - def gen_seal5_td(self, verbose: bool = False): - patch_name = "seal5_td" - dest = "llvm/lib/Target/RISCV/seal5.td" - dest2 = "llvm/lib/Target/RISCV/RISCV.td" - content = """ -// Includes -// seal5.td - seal5_td_includes - INSERTION_START -// seal5.td - seal5_td_includes - INSERTION_END -""" - content2 = """ -include "seal5.td" -""" - file_artifact = File( - dest, - content=content, - ) - patch_artifact = NamedPatch( - dest2, - key="riscv_td_includes", - content=content2, + llvm.build_llvm( + Path(self.settings.directory), self.settings.build_dir / config, cmake_options=cmake_options, target=target ) - artifacts = [file_artifact, patch_artifact] + logger.info("Completed build of Seal5 LLVM (%s)", target) - index_file = self.temp_dir / "seal5_td_index.yml" - write_index_yaml(index_file, artifacts, {}, content=True) - patch_settings = PatchSettings( - name=patch_name, - stage=int(PatchStage.PHASE_1), - comment="Add seal5.td to llvm/lib/Target/RISCV", - index=str(index_file), - generated=True, - target="llvm", - ) - self.settings.patches.append(patch_settings) - - def transform(self, verbose: bool = False): + def transform(self, verbose: bool = False, skip: Optional[List[str]] = None, only: Optional[List[str]] = None): logger.info("Tranforming Seal5 models") - inplace = True - if not inplace: - raise NotImplementedError() - # TODO: flow.models: Seal5ModelWrapper -> transform(verbose: bool = False, *kwargs) - # first convert M2-ISA-R MetaModel to Seal5 Metamodel - self.convert_models(verbose=verbose) - # filter model - self.filter_model(verbose=verbose) - # add aliases to model - # self.add_aliases(verbose=verbose) - # add intrinsics - # self.add_intrinsics(verbose=verbose) - # drop unused constants - self.drop_unused(verbose=verbose) - self.eliminate_rd_cmp_zero(verbose=verbose) - self.eliminate_mod_rfs(verbose=verbose) - self.drop_unused(verbose=verbose) - # optimize Seal5 Metamodel - self.optimize_model(verbose=verbose) - self.infer_types(verbose=verbose) - self.simplify_trivial_slices(verbose=verbose) - self.explicit_truncations(verbose=verbose) - self.process_settings(verbose=verbose) - self.write_yaml(verbose=verbose) - # determine dyn constraints (eliminate raise) - self.detect_behavior_constraints(verbose=verbose) - # determine operand types - self.collect_register_operands(verbose=verbose) - self.collect_immediate_operands(verbose=verbose) - self.collect_operand_types(verbose=verbose) - # detect side effects - self.detect_side_effects(verbose=verbose) - # detect ins/outs - self.detect_inouts(verbose=verbose) - # detect registers - self.detect_registers(verbose=verbose) - # TODO: determine static constraints (xlen,...) -> subtargetvmap - # detect memory adressing modes - # self.detect_adressing_modes(verbose) # TODO - # detect legal GMIR ops (and map to selectiondag?) - # self.detect_legal_ops(verbose=verbose) # TODO - # extract costs/heuristics - # self.extract_costs_and_heuristics(verbose) # TODO + passes_settings = self.settings.passes + assert passes_settings is not None + assert passes_settings.defaults is not None + default_skip = passes_settings.defaults.skip + if skip is None and default_skip: + skip = default_skip + default_only = passes_settings.defaults.only + if only is None and default_only: + only = default_only + # inplace = True + # if not inplace: + # raise NotImplementedError() + + input_models = self.settings.model_names + transform_passes = filter_passes(self.passes, pass_type=PassType.TRANSFORM) + with PassManager("transform_passes", transform_passes, skip=skip, only=only) as pm: + result = pm.run(input_models, settings=self.settings, env=self.prepare_environment(), verbose=verbose) + if result: + metrics = result.metrics + if metrics: + self.settings.metrics.append({pm.name: metrics}) + self.settings.save() logger.info("Completed tranformation of Seal5 models") - def generate(self, verbose: bool = False): + def generate(self, verbose: bool = False, skip: Optional[List[str]] = None, only: Optional[List[str]] = None): logger.info("Generating Seal5 patches") - # raise NotImplementedError - patches = [] - skip = [] # TODO: User, Global, PerInstr - # TODO: only: [] - - # Prerequisites - self.write_cdsl(verbose=verbose, split=True, compat=True) - self.gen_seal5_td(verbose=verbose) - - # # General - if "riscv_features" not in skip: - self.gen_riscv_features_patch() - if "riscv_isa_infos" not in skip: - self.gen_riscv_isa_info_patch() - # if "subtarget_tests" not in skip: - # patches.extend(self.gen_subtarget_tests_patches()) - - # # MC Level - # if "register_types" not in skip: - # patches.extend(self.gen_register_types_patches()) - # if "operand_types" not in skip: - # patches.extend(self.gen_operand_types_patches()) - # if "instruction_formats" not in skip: - # patches.extend(self.gen_instruction_format_patches()) - # if "instruction_infos" not in skip: - # patches.extend(self.gen_instruction_infos_patches()) - # if "disassembler" not in skip: - # patches.extend(self.gen_disassembler_patches()) - # if "mc_tests" not in skip: - # patches.extend(self.gen_mc_tests_patches()) - - # # Codegen Level - # if "selection_dag_legalizer" not in skip: - # patches.extend(self.gen_selection_dag_legalizer_patches()) - # if "globalisel_legalizer" not in skip: - # patches.extend(self.gen_globalisel_legalizer_patches()) - # if "scalar_costs" not in skip: - # patches.extend(self.gen_scalar_costs_patches()) - # if "simd_costs" not in skip: - # patches.extend(self.gen_simd_costs_patches()) - # if "isel_patterns" not in skip: - # patches.extend(self.gen_isel_patterns_patches()) - # if "codegen_test" not in skip: - # patches.extend(self.gen_codegen_tests_patches()) - - # Others - if "pattern_gen" not in skip: - self.convert_behav_to_llvmir_splitted(verbose=verbose) # TODO: add split arg - self.convert_llvmir_to_gmir_splitted(verbose=verbose) # TODO: add split arg - self.convert_behav_to_tablegen(verbose=verbose, split=True) + generate_passes = filter_passes(self.passes, pass_type=PassType.GENERATE) + # TODO: User, Global, PerInstr + input_models = self.settings.model_names + with PassManager("generate_passes", generate_passes, skip=skip, only=only) as pm: + result = pm.run(input_models, settings=self.settings, env=self.prepare_environment(), verbose=verbose) + if result: + metrics = result.metrics + if metrics: + self.settings.metrics.append({pm.name: metrics}) + self.settings.save() logger.info("Completed generation of Seal5 patches") @@ -1329,7 +450,7 @@ def collect_patches(self): name = patch_settings.name target = patch_settings.target key = (target, name) - dest = self.patches_dir / target + dest = self.settings.patches_dir / target patch_settings.to_yaml_file(dest / f"{name}.yml") # assert key not in temp if key in temp: @@ -1358,7 +479,7 @@ def collect_patches(self): temp[key] = patch_settings if patch_file: logger.debug("Copying custom patch_file %s", patch_file) - dest = self.patches_dir / target + dest = self.settings.patches_dir / target dest.mkdir(exist_ok=True) # print("dest", dest) utils.copy(patch_file, dest / f"{name}.patch") @@ -1380,7 +501,7 @@ def resolve_patch_file(self, path): ret = path if ret.is_file(): return path.resolve() - ret = self.patches_dir / path + ret = self.settings.patches_dir / path if ret.is_file(): return ret.resolve() raise RuntimeError(f"Patch file {path} not found!") @@ -1395,7 +516,7 @@ def apply_patch(self, patch: PatchSettings, force: bool = False): return if patch.index: # use inject_extensions_script - file = self.patches_dir / target / f"{name}.patch" + file = self.settings.patches_dir / target / f"{name}.patch" # assert not file.is_file(), f"Patch already exists: {file}" prefix = self.settings.git.prefix comment = patch.comment @@ -1439,13 +560,13 @@ def apply_patch(self, patch: PatchSettings, force: bool = False): repo.git.add(A=True) repo.index.commit(msg, author=actor) patch.applied = True - self.settings.to_yaml_file(self.settings_file) + self.settings.save() # TODO: commit def patch(self, verbose: bool = False, stages: List[PatchStage] = None, force: bool = False): logger.info("Applying Seal5 patches") if stages is None: - stages = list(map(PatchStage, range(PatchStage.PHASE_4))) + stages = list(map(PatchStage, range(PatchStage.PHASE_5 + 1))) assert len(stages) > 0 patches_per_stage = self.collect_patches() for stage in stages: @@ -1459,12 +580,15 @@ def patch(self, verbose: bool = False, stages: List[PatchStage] = None, force: b self.apply_patch(patch, force=force) logger.info("Completed application of Seal5 patches") - def test(self, debug: bool = False, verbose: bool = False, ignore_error: bool = False): + def test( + self, debug: bool = False, verbose: bool = False, ignore_error: bool = False, config: Optional[str] = None + ): logger.info("Testing Seal5 LLVM") - name = "debug" if debug else "release" + if config is None: + config = "debug" if debug else "release" test_paths = self.settings.test.paths failing_tests = llvm.test_llvm( - self.directory / "llvm" / "test", self.build_dir / name, test_paths, verbose=verbose + self.directory / "llvm" / "test", self.settings.build_dir / config, test_paths, verbose=verbose ) if len(failing_tests) > 0: logger.error("%d tests failed: %s", len(failing_tests), ", ".join(failing_tests)) @@ -1483,12 +607,18 @@ def export(self, dest: Path, verbose: bool = False): suffix = dest.suffix if suffix != ".gz": raise NotImplementedError("Only .tar.gz export is supported!") - artifacts = [self.inputs_dir, self.gen_dir, self.models_dir, self.logs_dir, self.settings_file] + artifacts = [ + self.settings.inputs_dir, + self.settings.gen_dir, + self.settings.models_dir, + self.settings.logs_dir, + self.settings.settings_file, + ] with tarfile.open(dest, mode="w:gz") as archive: for artifact in artifacts: name = str(artifact) - assert str(self.meta_dir) in name - name = name.replace(f"{self.meta_dir}/", "") + assert str(self.settings.meta_dir) in name + name = name.replace(f"{self.settings.meta_dir}/", "") if artifact.is_file(): archive.add(artifact, arcname=name) elif artifact.is_dir(): @@ -1504,18 +634,39 @@ def reset(self, settings: bool = True, verbose: bool = False, interactive: bool self.settings.reset() logger.info("Completed clean of Seal5 settings") - def clean(self, verbose: bool = False, interactive: bool = False): + def clean( + self, + temp: bool = False, + patches: bool = False, + models: bool = False, + inputs: bool = False, + logs: bool = False, + install: bool = False, + build: bool = False, + deps: bool = False, + verbose: bool = False, + interactive: bool = False, + ): logger.info("Cleaning Seal5 directories") - to_clean = [ - self.temp_dir, - self.gen_dir, - self.models_dir, - self.inputs_dir, - self.logs_dir, - self.install_dir, - self.build_dir, - self.deps_dir, - ] + to_clean = [] + if temp: + to_clean.append(self.settings.temp_dir) + if patches: + to_clean.append(self.settings.patches_dir) + if models: + to_clean.append(self.settings.models_dir) + if inputs: + to_clean.append(self.settings.inputs_dir) + if logs: + to_clean.append(self.settings.logs_dir) + if install: + to_clean.append(self.settings.install_dir) + if build: + to_clean.append(self.settings.build_dir) + if deps: + to_clean.append(self.settings.deps_dir) + # if gen: + # to_clean.append(self.settings.gen_dir) for path in to_clean: utils.clean_path(path, interactive=interactive) # self.reset(verbose=verbose, interactive=interactive) diff --git a/seal5/frontends/coredsl2_seal5/architecture_model_builder.py b/seal5/frontends/coredsl2_seal5/architecture_model_builder.py index c086470e..e4987353 100644 --- a/seal5/frontends/coredsl2_seal5/architecture_model_builder.py +++ b/seal5/frontends/coredsl2_seal5/architecture_model_builder.py @@ -247,12 +247,12 @@ def visitParameter_declaration(self, ctx: CoreDSL2Parser.Parameter_declarationCo # type is required, name and array size optional type_ = self.visit(ctx.type_) name = None - size = None + # size = None if ctx.decl: if ctx.decl.name: name = ctx.decl.name.text if ctx.decl.size: - size = [self.visit(obj) for obj in ctx.decl.size] + ctx.decl.size = [self.visit(obj) for obj in ctx.decl.size] p = arch.FnParam(name, type_._width, arch.DataType.S if type_.signed else arch.DataType.U) return p @@ -292,7 +292,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # extract storage type, qualifiers and attributes storage = [self.visit(obj) for obj in ctx.storage] - qualifiers = [self.visit(obj) for obj in ctx.qualifiers] + # qualifiers = [self.visit(obj) for obj in ctx.qualifiers] attributes = dict([self.visit(obj) for obj in ctx.attributes]) # extract data type diff --git a/seal5/frontends/coredsl2_seal5/behavior_model_builder.py b/seal5/frontends/coredsl2_seal5/behavior_model_builder.py index 551f3d5c..4c978ffb 100644 --- a/seal5/frontends/coredsl2_seal5/behavior_model_builder.py +++ b/seal5/frontends/coredsl2_seal5/behavior_model_builder.py @@ -108,9 +108,9 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): """ # extract variable qualifiers, currently unused - storage = [self.visit(obj) for obj in ctx.storage] - qualifiers = [self.visit(obj) for obj in ctx.qualifiers] - attributes = [self.visit(obj) for obj in ctx.attributes] + # storage = [self.visit(obj) for obj in ctx.storage] + # qualifiers = [self.visit(obj) for obj in ctx.qualifiers] + # attributes = [self.visit(obj) for obj in ctx.attributes] type_ = self.visit(ctx.type_) diff --git a/seal5/frontends/coredsl2_seal5/expr_interpreter.py b/seal5/frontends/coredsl2_seal5/expr_interpreter.py index 34ea365e..bdb9b5d5 100644 --- a/seal5/frontends/coredsl2_seal5/expr_interpreter.py +++ b/seal5/frontends/coredsl2_seal5/expr_interpreter.py @@ -8,7 +8,6 @@ """Very crude expression evaluation functions for use during model generation.""" -from m2isar import M2ValueError from m2isar.metamodel import arch, behav diff --git a/seal5/frontends/coredsl2_seal5/load_order.py b/seal5/frontends/coredsl2_seal5/load_order.py index 71d4162d..534d7b3f 100644 --- a/seal5/frontends/coredsl2_seal5/load_order.py +++ b/seal5/frontends/coredsl2_seal5/load_order.py @@ -8,7 +8,7 @@ from antlr4 import ParserRuleContext -from m2isar import M2DuplicateError, M2NameError +from m2isar import M2DuplicateError from .parser_gen import CoreDSL2Parser, CoreDSL2Visitor diff --git a/seal5/frontends/coredsl2_seal5/parser.py b/seal5/frontends/coredsl2_seal5/parser.py index 1d6caf69..b65c7b3d 100644 --- a/seal5/frontends/coredsl2_seal5/parser.py +++ b/seal5/frontends/coredsl2_seal5/parser.py @@ -31,7 +31,7 @@ def main(): args = parser.parse_args() - app_dir = pathlib.Path(__file__).parent.resolve() + # app_dir = pathlib.Path(__file__).parent.resolve() logging.basicConfig(level=getattr(logging, args.log.upper())) logger = logging.getLogger("parser") @@ -68,7 +68,7 @@ def main(): temp_save = {} # models: "dict[str, arch.CoreDef]" = {} - models: "dict[str arch.InstructionSet]" = {} + models: "dict[str, arch.InstructionSet]" = {} patch_model(expr_interpreter) @@ -81,7 +81,7 @@ def main(): s = [s] print("s", s) except M2Error as e: - logger.critical("Error building architecture model of core %s: %s", core_name, e) + logger.critical("Error building architecture model of core", e) for orig, overwritten in arch_builder._overwritten_instrs: logger.warning( @@ -150,7 +150,9 @@ def main(): op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: - logger.critical('error processing attribute "%s" of memory "%s": %s', attr_name, fn_def.name, e) + logger.critical( + 'error processing attribute "%s" of memory "%s": %s', attr_name, mem_def.name, e + ) sys.exit(1) mem_def.attributes[attr_name] = ops diff --git a/seal5/index.py b/seal5/index.py index 63a45607..19fd15be 100644 --- a/seal5/index.py +++ b/seal5/index.py @@ -98,9 +98,14 @@ def __init__( ): super().__init__(dest_path, src_path=src_path, content=content, append=append) - @property - def out_path(self): - return self.path + +class Directory(Artifact): + def __init__( + self, dest_path: Path, src_path: Optional[Path] = None, content: Optional[Path] = None, append: bool = False + ): + assert content is None + assert not append + super().__init__(dest_path, src_path=src_path, content=content, append=append) def write_index_yaml( diff --git a/seal5/pass_list.py b/seal5/pass_list.py new file mode 100644 index 00000000..21f3ec55 --- /dev/null +++ b/seal5/pass_list.py @@ -0,0 +1,1359 @@ +from typing import Optional + +from seal5 import utils +from seal5.tools import cdsl2llvm +from seal5.logging import get_logger +from seal5.index import File, NamedPatch, write_index_yaml +from seal5.passes import Seal5Pass, PassType, PassScope, PassManager, PassResult +from seal5.types import PatchStage +from seal5.settings import Seal5Settings, PatchSettings + +logger = get_logger() + + +def convert_models( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = False, + **kwargs, +): + assert not inplace + input_file = settings.models_dir / f"{input_model}.m2isarmodel" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + base = input_file.stem + metrics = {"foo": "bar"} + new_name = f"{base}.seal5model" + logger.info("Converting %s -> %s", name, new_name) + args = [ + settings.models_dir / name, + "-o", + settings.models_dir / new_name, + "--log", + "info", + # "debug", + ] + utils.python( + "-m", + "seal5.transform.converter", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + return PassResult(metrics=metrics) + + +def optimize_model( + input_model, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Optimizing %s", name) + args = [ + settings.models_dir / name, + "--log", + "info", + # "debug", + ] + utils.python( + "-m", + "seal5.transform.optimize_instructions.optimizer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def infer_types( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Infering types for %s", name) + args = [ + settings.models_dir / name, + "--log", + "info", + # "debug", + ] + utils.python( + "-m", + "seal5.transform.infer_types.transform", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def simplify_trivial_slices( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Simplifying trivial slices for %s", name) + args = [ + settings.models_dir / name, + "--log", + "info", + # "debug", + ] + utils.python( + "-m", + "seal5.transform.simplify_trivial_slices.transform", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def explicit_truncations( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Adding excplicit truncations for %s", name) + args = [ + settings.models_dir / name, + "--log", + "info", + # "debug", + ] + utils.python( + "-m", + "seal5.transform.explicit_truncations.transform", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def process_settings( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Processing settings for %s", name) + args = [ + settings.models_dir / name, + "--log", + "info", + # "debug", + "--yaml", + settings.settings_file, + ] + utils.python( + "-m", + "seal5.transform.process_settings.transform", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def filter_model( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Filtering %s", name) + filter_settings = settings.filter + filter_args = [] + + def get_filter_args(settings, suffix): + ret = [] + keep = list(map(str, settings.keep)) + drop = list(map(str, settings.drop)) + if keep: + ret += [f"--keep-{suffix}", ",".join(keep)] + if drop: + ret += [f"--drop-{suffix}", ",".join(drop)] + return ret + + filter_sets = filter_settings.sets + filter_instructions = filter_settings.instructions + filter_aliases = filter_settings.aliases + filter_intrinsics = filter_settings.intrinsics + filter_opcodes = filter_settings.opcodes + filter_encoding_sizes = filter_settings.encoding_sizes + # TODO: filter_functions + filter_args.extend(get_filter_args(filter_sets, "sets")) + filter_args.extend(get_filter_args(filter_instructions, "instructions")) + filter_args.extend(get_filter_args(filter_aliases, "aliases")) + filter_args.extend(get_filter_args(filter_intrinsics, "intrinsics")) + filter_args.extend(get_filter_args(filter_opcodes, "opcodes")) + filter_args.extend(get_filter_args(filter_encoding_sizes, "encoding-sizes")) + args = [ + settings.models_dir / name, + *filter_args, + "--log", + # "info", + "debug", + ] + utils.python( + "-m", + "seal5.transform.filter_model.filter", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def drop_unused( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Dropping unused for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + ] + utils.python( + "-m", + "seal5.transform.drop_unused.optimizer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def detect_registers( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Detecting registers for %s", name) + args = [ + settings.models_dir / name, + "--log", + "info", + # "debug", + ] + utils.python( + "-m", + "seal5.transform.detect_registers", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def detect_behavior_constraints( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Collecting constraints for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + ] + utils.python( + "-m", + "seal5.transform.collect_raises.collect", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def detect_side_effects( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Detecting side effects for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + ] + utils.python( + "-m", + "seal5.transform.detect_side_effects.collect", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def detect_inouts( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Detecting inouts for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + ] + utils.python( + "-m", + "seal5.transform.detect_inouts.collect", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def collect_operand_types( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Collecting operand types for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + "--skip-failing", + ] + utils.python( + "-m", + "seal5.transform.collect_operand_types.collect", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + # input("<") + + +def collect_register_operands( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Collecting register operands for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + ] + utils.python( + "-m", + "seal5.transform.collect_register_operands.collect", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def collect_immediate_operands( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Collecting immediate operands for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + ] + utils.python( + "-m", + "seal5.transform.collect_immediate_operands.collect", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def eliminate_rd_cmp_zero( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Eliminating rd == 0 for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + ] + utils.python( + "-m", + "seal5.transform.eliminate_rd_cmp_zero.transform", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def eliminate_mod_rfs( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + logger.info("Eliminating op MOD RFS for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + ] + utils.python( + "-m", + "seal5.transform.eliminate_mod_rfs.transform", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def write_yaml( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + **kwargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + new_name = name.replace(".seal5model", ".yml") + logger.info("Writing YAML for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + "--output", + settings.temp_dir / new_name, + ] + utils.python( + "-m", + "seal5.backends.yaml.writer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + new_settings: Seal5Settings = Seal5Settings.from_yaml_file(settings.temp_dir / new_name) + settings.merge(new_settings, overwrite=False) + + +def write_cdsl( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + inplace: bool = True, + split: bool = False, + compat: bool = False, + **kargs, +): + assert inplace + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + if split: + new_name = name.replace(".seal5model", "") + else: + new_name = name.replace(".seal5model", ".core_desc") + logger.info("Writing CDSL for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + "--output", + settings.temp_dir / new_name, + ] + if split: + (settings.temp_dir / new_name).mkdir(exist_ok=True) + args.append("--splitted") + if compat: + args.append("--compat") + utils.python( + "-m", + "seal5.backends.coredsl2.writer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + # args_compat = [ + # settings.models_dir / name, + # "--log", + # # "info", + # "debug", + # "--output", + # settings.temp_dir / f"{new_name}_compat", + # "--compat", + # ] + # if split: + # args.append("--splitted") + # utils.python( + # "-m", + # "seal5.backends.coredsl2.writer", + # *args_compat, + # env=env, + # print_func=logger.info if verbose else logger.debug, + # live=True, + # ) + + +# def write_cdsl_splitted(inplace: bool = True): +# assert inplace +# # input_files = list(settings.models_dir.glob("*.seal5model")) +# # assert len(input_files) > 0, "No Seal5 models found!" +# # for input_file in input_files: +# # name = input_file.name +# # sub = name.replace(".seal5model", "") +# for _ in [None]: +# set_names = list(settings.extensions.keys()) +# # print("set_names", set_names) +# assert len(set_names) > 0, "No sets found" +# for set_name in set_names: +# insn_names = settings.extensions[set_name].instructions +# if insn_names is None: +# logger.warning("Skipping empty set %s", set_name) +# continue +# sub = settings.extensions[set_name].model +# # TODO: populate model in yaml backend! +# if sub is None: # Fallback +# sub = set_name +# input_file = settings.models_dir / f"{sub}.seal5model" +# assert input_file.is_file(), f"File not found: {input_file}" +# assert len(insn_names) > 0, f"No instructions found in set: {set_name}" +# # insn_names = collect_instr_names() +# (settings.temp_dir / sub / set_name).mkdir(exist_ok=True, parents=True) +# for insn_name in insn_names: +# logger.info("Writing Metamodel for %s/%s/%s", sub, set_name, insn_name) +# args = [ +# input_file, +# "--keep-instructions", +# insn_name, +# "--log", +# "debug", +# # "info", +# "--output", +# settings.temp_dir / sub / set_name / f"{insn_name}.seal5model", +# ] +# utils.python( +# "-m", +# "seal5.transform.filter_model.filter", +# *args, +# env=env, +# print_func=logger.info if verbose else logger.debug, +# live=True, +# ) +# logger.info("Writing CDSL for %s/%s", sub, insn_name) +# args = [ +# settings.temp_dir / sub / set_name / f"{insn_name}.seal5model", +# "--log", +# "debug", +# # "info", +# "--output", +# settings.temp_dir / sub / set_name / f"{insn_name}.core_desc" +# ] +# utils.python( +# "-m", +# "seal5.backends.coredsl2.writer", +# *args, +# env=env, +# print_func=logger.info if verbose else logger.debug, +# live=True, +# ) +# args_compat = [ +# settings.temp_dir / sub / set_name / f"{insn_name}.seal5model", +# "--log", +# # "info", +# "debug", +# "--output", +# settings.temp_dir / sub / set_name / f"{insn_name}.core_desc_compat", +# "--compat", +# ] +# utils.python( +# "-m", +# "seal5.backends.coredsl2.writer", +# *args_compat, +# env=env, +# print_func=logger.info if verbose else logger.debug, +# live=True, +# ) + + +def convert_behav_to_llvmir( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + split: bool = True, + **kwargs, +): + assert split, "TODO" + gen_metrics_file = True + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + if split: + new_name = name.replace(".seal5model", "") + else: + new_name = name.replace(".seal5model", ".ll") + logger.info("Writing LLVM-IR for %s", name) + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + "--output", + settings.temp_dir / new_name, + ] + if split: + (settings.temp_dir / new_name).mkdir(exist_ok=True) + args.append("--splitted") + if gen_metrics_file: + # TODO: move to .seal5/metrics + metrics_file = settings.temp_dir / (new_name + "_llvmir_metrics.csv") + args.extend(["--metrics", metrics_file]) + utils.python( + "-m", + "seal5.backends.llvmir.writer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + + +def convert_behav_to_tablegen( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + split: bool = True, + formats: bool = True, + patterns: bool = True, + **kwargs, +): + assert split, "TODO" + gen_metrics_file = True + gen_index_file = True + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + if split: + new_name = name.replace(".seal5model", "") + else: + new_name = name.replace(".seal5model", ".td") + logger.info("Writing TableGen patterns for %s", name) + # TODO: move to patches dir! + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + "--output", + settings.temp_dir / new_name, + ] + if split: + (settings.temp_dir / new_name).mkdir(exist_ok=True) + args.append("--splitted") + if formats: + args.append("--formats") + if patterns: + args.append("--patterns") + if gen_metrics_file: + metrics_file = settings.temp_dir / (new_name + "_tblgen_patterns_metrics.csv") + args.extend(["--metrics", metrics_file]) + if gen_index_file: + index_file = settings.temp_dir / (new_name + "_tblgen_patterns_index.yml") + args.extend(["--index", index_file]) + utils.python( + "-m", + "seal5.backends.patterngen.writer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + if gen_index_file: + if index_file.is_file(): + patch_name = f"tblgen_patterns_{input_file.stem}" + patch_settings = PatchSettings( + name=patch_name, + stage=int(PatchStage.PHASE_4), + comment=f"CDSL2LLVM Generated Tablegen Patterns for {input_file.name}", + index=str(index_file), + generated=True, + target="llvm", + ) + settings.add_patch(patch_settings) + settings.to_yaml_file(settings.settings_file) + else: + logger.warning("No patches found!") + + +def gen_riscv_features_patch( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + split: bool = False, + **kwargs, +): + assert not split, "TODO" + # formats = True + gen_metrics_file = True + gen_index_file = True + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + new_name = name.replace(".seal5model", "") + logger.info("Writing RISCVFeatures.td patch for %s", name) + out_dir = settings.patches_dir / new_name + out_dir.mkdir(exist_ok=True) + + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + "--output", + out_dir / "riscv_features.patch", + ] + if split: + args.append("--splitted") + if gen_metrics_file: + metrics_file = out_dir / ("riscv_features_metrics.csv") + args.extend(["--metrics", metrics_file]) + if gen_index_file: + index_file = out_dir / ("riscv_features_index.yml") + args.extend(["--index", index_file]) + utils.python( + "-m", + "seal5.backends.riscv_features.writer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + if gen_index_file: + if index_file.is_file(): + patch_name = f"riscv_features_{input_file.stem}" + patch_settings = PatchSettings( + name=patch_name, + stage=int(PatchStage.PHASE_2), + comment=f"Generated RISCVFeatures.td patch for {input_file.name}", + index=str(index_file), + generated=True, + target="llvm", + ) + settings.add_patch(patch_settings) + settings.to_yaml_file(settings.settings_file) + else: + logger.warning("No patches found!") + + +def gen_riscv_isa_info_patch( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + split: bool = False, + **kwargs, +): + assert not split, "TODO" + # formats = True + gen_metrics_file = True + gen_index_file = True + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + new_name = name.replace(".seal5model", "") + logger.info("Writing RISCVISAInfo.cpp patch for %s", name) + out_dir = settings.patches_dir / new_name + out_dir.mkdir(exist_ok=True) + + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + "--output", + out_dir / "riscv_isa_info.patch", + ] + if split: + args.append("--splitted") + if gen_metrics_file: + metrics_file = out_dir / ("riscv_isa_info_metrics.csv") + args.extend(["--metrics", metrics_file]) + if gen_index_file: + index_file = out_dir / ("riscv_isa_info_index.yml") + args.extend(["--index", index_file]) + utils.python( + "-m", + "seal5.backends.riscv_isa_info.writer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + if gen_index_file: + if index_file.is_file(): + patch_name = f"riscv_isa_info_{input_file.stem}" + patch_settings = PatchSettings( + name=patch_name, + stage=int(PatchStage.PHASE_1), + comment=f"Generated RISCVISAInfo.cpp patch for {input_file.name}", + index=str(index_file), + generated=True, + target="llvm", + ) + settings.add_patch(patch_settings) + settings.to_yaml_file(settings.settings_file) + else: + logger.warning("No patches found!") + + +def gen_riscv_instr_info_patch( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + split: bool = True, + **kwargs, +): + # assert not split, "TODO" + assert split, "TODO" + # formats = True + gen_metrics_file = True + gen_index_file = True + input_file = settings.models_dir / f"{input_model}.seal5model" + assert input_file.is_file(), f"File not found: {input_file}" + name = input_file.name + new_name = name.replace(".seal5model", "") + logger.info("Writing RISCVInstrInfo.td patch for %s", name) + out_dir = settings.patches_dir / new_name + out_dir.mkdir(exist_ok=True) + if split: + out_path = out_dir / "riscv_instr_info" + out_path.mkdir(exist_ok=True) + else: + out_path = out_dir / "riscv_instr_info.patch" + + args = [ + settings.models_dir / name, + "--log", + # "info", + "debug", + "--output", + out_path, + ] + if split: + args.append("--splitted") + if gen_metrics_file: + metrics_file = out_dir / ("riscv_instr_info_metrics.csv") + args.extend(["--metrics", metrics_file]) + if gen_index_file: + index_file = out_dir / ("riscv_instr_info_index.yml") + args.extend(["--index", index_file]) + utils.python( + "-m", + "seal5.backends.riscv_instr_info.writer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + if gen_index_file: + if index_file.is_file(): + patch_name = f"riscv_instr_info_{input_file.stem}" + patch_settings = PatchSettings( + name=patch_name, + stage=int(PatchStage.PHASE_2), + comment=f"Generated RISCVInstrInfo.td patch for {input_file.name}", + index=str(index_file), + generated=True, + target="llvm", + ) + settings.add_patch(patch_settings) + settings.to_yaml_file(settings.settings_file) + else: + logger.warning("No patches found!") + + +def gen_riscv_gisel_legalizer_patch( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + **kwargs, +): + gen_metrics_file = False # TODO + gen_index_file = True + assert input_model is None + # input_file = settings.models_dir / f"{input_model}.seal5model" + # assert input_file.is_file(), f"File not found: {input_file}" + # name = input_file.name + # new_name = name.replace(".seal5model", "") + logger.info("Writing RISCVLegalizerInfo.cpp patch") + out_dir = settings.patches_dir / "Seal5" + out_dir.mkdir(exist_ok=True) + + args = [ + "none", + "--yaml", + settings.settings_file, + "--log", + # "info", + "debug", + "--output", + out_dir / "riscv_gisel_legalizer.patch", + ] + if gen_metrics_file: + metrics_file = out_dir / ("riscv_gisel_legalizer_metrics.csv") + args.extend(["--metrics", metrics_file]) + if gen_index_file: + index_file = out_dir / ("riscv_gisel_legalizer_index.yml") + args.extend(["--index", index_file]) + utils.python( + "-m", + "seal5.backends.riscv_gisel_legalizer.writer", + *args, + env=env, + print_func=logger.info if verbose else logger.debug, + live=True, + ) + if gen_index_file: + if index_file.is_file(): + patch_name = "riscv_gisel_legalizer" + patch_settings = PatchSettings( + name=patch_name, + stage=int(PatchStage.PHASE_1), + comment="Generated RISCVLegalizerInfo.cpp patch", + index=str(index_file), + generated=True, + target="llvm", + ) + settings.add_patch(patch_settings) + settings.to_yaml_file(settings.settings_file) + else: + logger.warning("No patches found!") + # TODO: introduce global (model-independed) settings file + + +# def convert_behav_to_tablegen_splitted(inplace: bool = True): +# assert inplace +# # input_files = list(settings.models_dir.glob("*.seal5model")) +# # assert len(input_files) > 0, "No Seal5 models found!" +# errs = [] +# # for input_file in input_files: +# # name = input_file.name +# # sub = name.replace(".seal5model", "") +# for _ in [None]: +# set_names = list(settings.extensions.keys()) +# assert len(set_names) > 0, "No sets found" +# for set_name in set_names: +# insn_names = settings.extensions[set_name].instructions +# if insn_names is None: +# logger.warning("Skipping empty set %s", set_name) +# continue +# assert len(insn_names) > 0, f"No instructions found in set: {set_name}" +# sub = settings.extensions[set_name].model +# # TODO: populate model in yaml backend! +# if sub is None: # Fallbacke +# sub = set_name +# for insn_name in insn_names: +# ll_file = settings.temp_dir / sub / set_name / f"{insn_name}.ll" +# if not ll_file.is_file(): +# logger.warning("Skipping %s due to errors.", insn_name) +# continue +# # input_file = settings.temp_dir / sub / set_name / f"{insn_name}.core_desc_compat" +# input_file = settings.temp_dir / sub / set_name / f"{insn_name}.core_desc" +# assert input_file.is_file(), f"File not found: {input_file}" +# output_file = input_file.parent / (input_file.stem + ".td") +# name = input_file.name +# logger.info("Writing TableGen for %s", name) +# ext = settings.extensions[set_name].feature +# if ext is None: # fallback to set_name +# ext = set_name.replace("_", "") +# try: +# cdsl2llvm.run_pattern_gen(settings.deps_dir / "cdsl2llvm" / "llvm" / "build", +# input_file, output_file, +# skip_patterns=False, skip_formats=False, ext=ext) +# except AssertionError: +# pass +# # errs.append((insn_name, str(ex))) +# if len(errs) > 0: +# # print("errs", errs) +# for insn_name, err_str in errs: +# print("Err:", insn_name, err_str) +# input("!") + + +def convert_llvmir_to_gmir( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + split: bool = True, + inplace: bool = True, + **kwargs, +): + assert inplace + assert split + # input_files = list(settings.models_dir.glob("*.seal5model")) + # assert len(input_files) > 0, "No Seal5 models found!" + errs = [] + # for input_file in input_files: + # name = input_file.name + # sub = name.replace(".seal5model", "") + for _ in [None]: + set_names = list(settings.extensions.keys()) + assert len(set_names) > 0, "No sets found" + for set_name in set_names: + insn_names = settings.extensions[set_name].instructions + if insn_names is None: + logger.warning("Skipping empty set %s", set_name) + continue + assert len(insn_names) > 0, f"No instructions found in set: {set_name}" + sub = settings.extensions[set_name].model + if sub != input_model: + continue + # TODO: populate model in yaml backend! + if sub is None: # Fallbacke + sub = set_name + for insn_name in insn_names: + ll_file = settings.temp_dir / sub / set_name / f"{insn_name}.ll" + if not ll_file.is_file(): + logger.warning("Skipping %s due to errors.", insn_name) + continue + output_file = ll_file.parent / (ll_file.stem + ".gmir") + name = ll_file.name + logger.info("Writing gmir for %s", name) + try: + # TODO: move to backends + cdsl2llvm_build_dir = None + integrated_pattern_gen = settings.tools.pattern_gen.integrated + if integrated_pattern_gen: + config = "release" # TODO: fetch default config + cdsl2llvm_build_dir = str(settings.build_dir / config) + else: + cdsl2llvm_build_dir = str(settings.deps_dir / "cdsl2llvm" / "llvm" / "build") + cdsl2llvm.convert_ll_to_gmir( + # settings.deps_dir / "cdsl2llvm" / "llvm" / "build", ll_file, output_file + cdsl2llvm_build_dir, + ll_file, + output_file, + ) + except AssertionError: + pass + # errs.append((insn_name, str(ex))) + if len(errs) > 0: + # print("errs", errs) + for insn_name, err_str in errs: + print("Err:", insn_name, err_str) + input("!") + + +def gen_seal5_td( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + **kwargs, +): + assert input_model is None + patch_name = "seal5_td" + dest = "llvm/lib/Target/RISCV/seal5.td" + dest2 = "llvm/lib/Target/RISCV/RISCV.td" + content = """ +// Includes +// seal5.td - seal5_td_includes - INSERTION_START +// seal5.td - seal5_td_includes - INSERTION_END +""" + content2 = """ +include "seal5.td" +""" + + file_artifact = File( + dest, + content=content, + ) + patch_artifact = NamedPatch( + dest2, + key="riscv_td_includes", + content=content2, + ) + artifacts = [file_artifact, patch_artifact] + + index_file = settings.temp_dir / "seal5_td_index.yml" + write_index_yaml(index_file, artifacts, {}, content=True) + patch_settings = PatchSettings( + name=patch_name, + stage=int(PatchStage.PHASE_1), + comment="Add seal5.td to llvm/lib/Target/RISCV", + index=str(index_file), + generated=True, + target="llvm", + ) + settings.add_patch(patch_settings) + + +def gen_model_td( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + **kwargs, +): + assert input_model is not None + patch_name = f"model_td_{input_model}" + dest = f"llvm/lib/Target/RISCV/seal5/{input_model}.td" + dest2 = "llvm/lib/Target/RISCV/seal5.td" + content = f""" +// Includes +// {input_model}.td - {input_model.lower()}_td_includes - INSERTION_START +// {input_model}.td - {input_model.lower()}_td_includes - INSERTION_END +""" + content2 = f""" +include "seal5/{input_model}.td" +""" + + file_artifact = File( + dest, + content=content, + ) + patch_artifact = NamedPatch( + dest2, + key=f"{input_model.lower()}_td_includes", + content=content2, + ) + artifacts = [file_artifact, patch_artifact] + + index_file = settings.temp_dir / "model_td_index.yml" + write_index_yaml(index_file, artifacts, {}, content=True) + patch_settings = PatchSettings( + name=patch_name, + stage=int(PatchStage.PHASE_1), + comment=f"Add {input_model}.td to llvm/lib/Target/RISCV/seal5", + index=str(index_file), + generated=True, + target="llvm", + ) + settings.add_patch(patch_settings) + + +def gen_set_td( + input_model: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + **kwargs, +): + assert input_model is not None + artifacts = [] + includes = [] + for set_name, set_settings in settings.extensions.items(): + if set_settings.model != input_model: + continue + set_name_lower = set_name.lower() + patch_name = f"set_td_{set_name}" + dest = f"llvm/lib/Target/RISCV/seal5/{set_name}.td" + dest2 = "llvm/lib/Target/RISCV/seal5.td" + content = f""" +// Includes +// {set_name}.td - {set_name_lower}_set_td_includes - INSERTION_START +// {set_name}.td - {set_name_lower}_set_td_includes - INSERTION_END +""" + includes.append(f"seal5/{set_name}.td") + file_artifact = File( + dest, + content=content, + ) + artifacts.append(file_artifact) + content2 = f"// {input_model}\n" + "\n".join([f'include "{inc}"' for inc in includes]) + "\n" + patch_artifact = NamedPatch( + dest2, + key="seal5_td_includes", + content=content2.strip(), + ) + artifacts.append(patch_artifact) + + index_file = settings.temp_dir / f"{input_model}_set_td_index.yml" + write_index_yaml(index_file, artifacts, {}, content=True) + patch_settings = PatchSettings( + name=patch_name, + stage=int(PatchStage.PHASE_1), + comment=f"Add {input_model} set includes to llvm/lib/Target/RISCV/seal5", + index=str(index_file), + generated=True, + target="llvm", + ) + settings.add_patch(patch_settings) + + +def pattern_gen_pass( + model_name: str, + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + split: bool = True, + **kwargs, +): + assert settings is not None, "Pass needs settings." + llvm_version = settings.llvm.state.version + assert llvm_version is not None + if llvm_version.major < 18: + raise RuntimeError("PatternGen needs LLVM version 18 or higher") + PATTERN_GEN_PASSES = [ + ("write_cdsl_compat", write_cdsl, {"split": split, "compat": True}), + ("behav_to_llvmir", convert_behav_to_llvmir, {"split": split}), + ("llvmir_to_gmir", convert_llvmir_to_gmir, {"split": split}), + # ("write_fmt", convert_behav_to_tablegen, {"split": split, "formats": True, "patterns": False}), + ("behav_to_pat", convert_behav_to_tablegen, {"split": split, "formats": False, "patterns": True}), + ] + pass_list = [] + for pass_name, pass_handler, pass_options in PATTERN_GEN_PASSES: + pass_list.append(Seal5Pass(pass_name, PassType.GENERATE, PassScope.MODEL, pass_handler, options=pass_options)) + # TODO: get parent pass context automatically + parent = kwargs.get("parent", None) + assert parent is not None + with PassManager("pattern_gen_passes", pass_list, skip=[], only=[], parent=parent) as pm: + result = pm.run([model_name], settings=settings, env=env, verbose=verbose) + return result diff --git a/seal5/passes.py b/seal5/passes.py new file mode 100644 index 00000000..54d5db98 --- /dev/null +++ b/seal5/passes.py @@ -0,0 +1,238 @@ +import os +import time +import multiprocessing +from pathlib import Path +from enum import Enum, IntFlag, auto +from dataclasses import dataclass +from typing import Callable, Optional, List +from concurrent.futures import ThreadPoolExecutor + +from seal5.logging import get_logger +from seal5.settings import Seal5Settings + +logger = get_logger() + + +NUM_THREADS = int(os.environ.get("SEAL5_NUM_THREADS", multiprocessing.cpu_count())) + + +class PassFormat(IntFlag): + NONE = auto() + CDSL = auto() + YAML = auto() + M2ISAR = auto() + SEAL5 = auto() + LLVMIR = auto() + GMIR = auto() + SRC = auto() + + +class PassType(Enum): + TRANSFORM = auto() + GENERATE = auto() + + +class PassStatus(Enum): + CREATED = auto() + RUNNING = auto() + FAILED = auto() + COMPLETED = auto() + SKIPPED = auto() + + +class PassScope(Enum): + GLOBAL = auto() + MODEL = auto() + SET = auto() # not supported yet + INSTR = auto() # not supported yet + + +@dataclass +class PassResult: + metrics: Optional[dict] = None + outputs: Optional[List[Path]] = None + + +def check_filter(name, skip, only): + if skip is None: + skip = [] + if only is None: + only = [] + if not ((name in only or len(only) == 0) and (name not in skip or len(skip) == 0)): + return True + return False + + +class Seal5Pass: + def __init__(self, name, pass_type, pass_scope, handler, fmt=PassFormat.NONE, order=-1, options=None): + self.name: str = name + self.pass_type: PassType = pass_type + self.pass_scope: PassScope = pass_scope + self.handler: Callable = handler + self.fmt: PassFormat = fmt + self.status = PassStatus.CREATED + self.order: int = order # not supported yet + self.options: Optional[dict] = options + self.metrics: dict = {} + + def __repr__(self): + return f"Seal5Pass({self.name}, {self.pass_type}, {self.pass_scope})" + + @property + def is_pending(self): + return self.status in [PassStatus.CREATED, PassStatus.SKIPPED] + + def skip(self): + self.status = PassStatus.SKIPPED + + def run(self, inputs: List[str], *args, settings: Optional[Seal5Settings] = None, **kwargs): + logger.debug("Running pass: %s", self) + self.status = PassStatus.RUNNING + self.metrics["models"] = [] + try: + kwargs_ = {**kwargs} + if self.options: + kwargs_.update(self.options) + start = time.time() + parent = kwargs.get("parent", None) + if parent: + parallel = parent.parallel + else: + parallel = 1 + if self.pass_scope == PassScope.MODEL: + assert settings is not None + passes_settings = settings.passes + assert passes_settings is not None + assert passes_settings.per_model is not None + + with ThreadPoolExecutor(max_workers=parallel) as executor: + futures = [] + for input_model in inputs: + kwargs__ = kwargs_.copy() + per_model = passes_settings.per_model.get(input_model, None) + if per_model: + if check_filter(self.name, per_model.skip, per_model.only): + logger.info("Skipped pass %s for model %s", self.name, input_model) + continue + if per_model.overrides: + overrides = per_model.overrides.get(self.name, None) + if overrides: + kwargs__.update(overrides) + future = executor.submit(self.handler, input_model, settings=settings, **kwargs__) + futures.append(future) + results = [] + for i, future in enumerate(futures): + result = future.result() + input_model = inputs[i] + if result: + metrics = result.metrics + if metrics: + self.metrics["models"].append({input_model: metrics}) + results.append(result) + # TODO: check results (metrics?) + elif self.pass_scope == PassScope.GLOBAL: + input_model = None + result = self.handler(input_model, settings=settings, **kwargs_) + if result: + metrics = result.metrics + if metrics: + self.metrics["models"].append({input_model: metrics}) + else: + raise NotImplementedError + end = time.time() + diff = end - start + self.status = PassStatus.COMPLETED + self.metrics["time_s"] = diff + except Exception as e: + self.status = PassStatus.FAILED + raise e + return PassResult(metrics=self.metrics) + + +class PassManager: + def __init__( + self, + name: str, + pass_list: List[Seal5Pass], + skip: Optional[List[str]] = None, + only: Optional[List[str]] = None, + parent: Optional["PassManager"] = None, + parallel: Optional[int] = None, + ): + self.name = name + self.pass_list = pass_list + self.skip = skip if skip is not None else (parent.skip if parent else []) + self.only = only if only is not None else (parent.only if parent else []) + self.parallel = parallel if parallel is not None else (parent.parallel if parent else NUM_THREADS) + self.metrics: dict = {} + self.open: bool = False + + @property + def size(self): + return len(self.pass_list) + + def __enter__(self): + assert not self.open + self.open = True + logger.debug("Processing %d passes...", self.size) + return self + + def __exit__(self, *exc): + self.open = False + logger.debug("Done.") + # return False + + def run( + self, + input_models: List[str], + settings: Optional[Seal5Settings] = None, + env: Optional[dict] = None, + verbose: bool = False, + ): + assert self.open, "PassManager needs context" + start = time.time() + self.metrics["passes"] = [] + # passes_settings = settings.passes + # assert passes_settings is not None + # assert passes_settings.per_model is not None + + for pass_ in self.pass_list: + # input_models_ = [] + # for model_name in input_models: + # overrides = passes_settings.per_model.get(model_name, None) + # if overrides: + if check_filter(pass_.name, self.skip, self.only): + pass_.skip() + continue + assert pass_.is_pending, f"Pass {pass_.name} is not pending" + result = pass_.run(input_models, settings=settings, env=env, verbose=verbose, parent=self) + if result: + metrics = result.metrics + if metrics: + self.metrics["passes"].append({pass_.name: metrics}) + end = time.time() + diff = end - start + self.metrics["time_s"] = diff + return PassResult(metrics=self.metrics) + + +def filter_passes( + passes: List[Seal5Pass], + pass_name: Optional[str] = None, + pass_type: Optional[PassType] = None, + pass_scope: Optional[PassScope] = None, +): + # TODO: support regex patters in name + ret = [] + for pass_ in passes: + if pass_name is not None: + if pass_.name != pass_name: + continue + if pass_type is not None: + if pass_.pass_type != pass_type: + continue + if pass_scope is not None: + if pass_.pass_scope != pass_scope: + continue + ret.append(pass_) + return ret diff --git a/seal5/resources/lit.cfg.py b/seal5/resources/lit.cfg.py new file mode 100644 index 00000000..c5d61933 --- /dev/null +++ b/seal5/resources/lit.cfg.py @@ -0,0 +1,18 @@ +# -*- Python -*- + +# Configuration file for the 'lit' test runner. + +import lit.util +import lit.formats +from lit.llvm import llvm_config + +# name: The name of this test suite. +config.name = "LLVM" + +# testFormat: The test format to use to interpret tests. +# config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) +config.test_format = lit.formats.ShTest(True) + +# # suffixes: A list of file extensions to treat as test files. This is overriden +# # by individual lit.local.cfg files in the test subdirectories. +config.suffixes = [".ll", ".c", ".test", ".txt", ".s", ".mir", ".yaml"] diff --git a/seal5/resources/patches/llvm/insert_markers_llvm18.patch b/seal5/resources/patches/llvm/insert_markers_llvm18.patch new file mode 100644 index 00000000..87e224ef --- /dev/null +++ b/seal5/resources/patches/llvm/insert_markers_llvm18.patch @@ -0,0 +1,211 @@ +diff --git a/clang/include/clang/Basic/BuiltinsRISCV.def b/clang/include/clang/Basic/BuiltinsRISCV.def +--- a/clang/include/clang/Basic/BuiltinsRISCV.def ++++ b/clang/include/clang/Basic/BuiltinsRISCV.def +@@ -89,5 +89,8 @@ TARGET_BUILTIN(__builtin_riscv_sm3p1, "UiUi", "nc", "zksh") + TARGET_BUILTIN(__builtin_riscv_ntl_load, "v.", "t", "zihintntl") + TARGET_BUILTIN(__builtin_riscv_ntl_store, "v.", "t", "zihintntl") + ++// BuiltinsRISCV.def - builtins_riscv - INSERTION_START ++// BuiltinsRISCV.def - builtins_riscv - INSERTION_END ++ + #undef BUILTIN + #undef TARGET_BUILTIN +diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp +--- a/clang/lib/CodeGen/CGBuiltin.cpp ++++ b/clang/lib/CodeGen/CGBuiltin.cpp +@@ -21243,6 +21243,9 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID, + ID = Intrinsic::riscv_sha256sum1; + break; + ++// CGBuiltin.cpp - cg_builtin - INSERTION_START ++// CGBuiltin.cpp - cg_builtin - INSERTION_END ++ + // Zksed + case RISCV::BI__builtin_riscv_sm4ks: + ID = Intrinsic::riscv_sm4ks; +diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td +--- a/llvm/include/llvm/IR/IntrinsicsRISCV.td ++++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td +@@ -1882,6 +1882,9 @@ let TargetPrefix = "riscv" in { + // Zvksh + def int_riscv_vsm3c : RISCVBinaryAAXUnMaskedZvk; + def int_riscv_vsm3me : RISCVBinaryAAXUnMasked; ++ ++// IntrinsicsRISCV.td - intrinsics_riscv - INSERTION_START ++// IntrinsicsRISCV.td - intrinsics_riscv - INSERTION_END + } // TargetPrefix = "riscv" + + // Vendor extensions +diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h +--- a/llvm/include/llvm/InitializePasses.h ++++ b/llvm/include/llvm/InitializePasses.h +@@ -313,6 +313,8 @@ void initializeWasmEHPreparePass(PassRegistry&); + void initializeWinEHPreparePass(PassRegistry&); + void initializeWriteBitcodePassPass(PassRegistry&); + void initializeXRayInstrumentationPass(PassRegistry&); ++// InitializePasses.h - initialize_passes_decl - INSERTION_START ++// InitializePasses.h - initialize_passes_decl - INSERTION_END + + } // end namespace llvm + +diff --git a/llvm/lib/CodeGen/GlobalISel/CMakeLists.txt b/llvm/lib/CodeGen/GlobalISel/CMakeLists.txt +--- a/llvm/lib/CodeGen/GlobalISel/CMakeLists.txt ++++ b/llvm/lib/CodeGen/GlobalISel/CMakeLists.txt +@@ -24,6 +24,8 @@ add_llvm_component_library(LLVMGlobalISel + MachineIRBuilder.cpp + RegBankSelect.cpp + Utils.cpp ++ # CMakeLists.txt - gisel_cmake_srcs - INSERTION_START ++ # CMakeLists.txt - gisel_cmake_srcs - INSERTION_END + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/CodeGen/GlobalISel +diff --git a/llvm/lib/CodeGen/GlobalISel/GlobalISel.cpp b/llvm/lib/CodeGen/GlobalISel/GlobalISel.cpp +--- a/llvm/lib/CodeGen/GlobalISel/GlobalISel.cpp ++++ b/llvm/lib/CodeGen/GlobalISel/GlobalISel.cpp +@@ -21,4 +21,6 @@ void llvm::initializeGlobalISel(PassRegistry &Registry) { + initializeLocalizerPass(Registry); + initializeRegBankSelectPass(Registry); + initializeInstructionSelectPass(Registry); ++ // GlobalISel.cpp - gisel_init - INSERTION_START ++ // GlobalISel.cpp - gisel_init - INSERTION_END + } +diff --git a/llvm/lib/Support/RISCVISAInfo.cpp b/llvm/lib/Support/RISCVISAInfo.cpp +--- a/llvm/lib/Support/RISCVISAInfo.cpp ++++ b/llvm/lib/Support/RISCVISAInfo.cpp +@@ -89,6 +89,9 @@ static const RISCVSupportedExtension SupportedExtensions[] = { + {"xtheadvdot", {1, 0}}, + {"xventanacondops", {1, 0}}, + ++// RISCVISAInfo.cpp - riscv_isa_info - INSERTION_START ++// RISCVISAInfo.cpp - riscv_isa_info - INSERTION_END ++ + {"za128rs", {1, 0}}, + {"za64rs", {1, 0}}, + {"zawrs", {1, 0}}, +@@ -192,6 +195,8 @@ static const RISCVSupportedExtension SupportedExtensions[] = { + + // NOTE: This table should be sorted alphabetically by extension name. + static const RISCVSupportedExtension SupportedExperimentalExtensions[] = { ++// RISCVISAInfo.cpp - riscv_isa_info_experimental - INSERTION_START ++// RISCVISAInfo.cpp - riscv_isa_info_experimental - INSERTION_END + {"zacas", {1, 0}}, + + {"zcmop", {0, 2}}, +diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp ++++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +@@ -696,6 +696,8 @@ public: + bool isUImm7() const { return IsUImm<7>(); } + bool isUImm8() const { return IsUImm<8>(); } + bool isUImm20() const { return IsUImm<20>(); } ++ // RISCVAsmParser.cpp - riscv_operands - INSERTION_START ++ // RISCVAsmParser.cpp - riscv_operands - INSERTION_END + + bool isUImm8GE32() const { + int64_t Imm; +diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp +--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp ++++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp +@@ -75,6 +75,9 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST) + + using namespace TargetOpcode; + ++// RISCVLegalizerInfo.cpp - riscv_legalizer_info - INSERTION_START ++// RISCVLegalizerInfo.cpp - riscv_legalizer_info - INSERTION_END ++ + auto AllVecTys = {nxv1s8, nxv2s8, nxv4s8, nxv8s8, nxv16s8, nxv32s8, + nxv64s8, nxv1s16, nxv2s16, nxv4s16, nxv8s16, nxv16s16, + nxv32s16, nxv1s32, nxv2s32, nxv4s32, nxv8s32, nxv16s32, +diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h +--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h ++++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h +@@ -309,6 +309,8 @@ enum OperandType : unsigned { + OPERAND_CLUI_IMM, + OPERAND_VTYPEI10, + OPERAND_VTYPEI11, ++ // RISCVBaseInfo.h - riscv_operands - INSERTION_START ++ // RISCVBaseInfo.h - riscv_operands - INSERTION_END + OPERAND_RVKRNUM, + OPERAND_RVKRNUM_0_7, + OPERAND_RVKRNUM_1_10, +diff --git a/llvm/lib/Target/RISCV/RISCV.td b/llvm/lib/Target/RISCV/RISCV.td +--- a/llvm/lib/Target/RISCV/RISCV.td ++++ b/llvm/lib/Target/RISCV/RISCV.td +@@ -74,3 +74,6 @@ def RISCV : Target { + let AssemblyWriters = [RISCVAsmWriter]; + let AllowRegisterRenaming = 1; + } ++ ++// RISCV.td - riscv_td_includes - INSERTION_START ++// RISCV.td - riscv_td_includes - INSERTION_END +diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td +--- a/llvm/lib/Target/RISCV/RISCVFeatures.td ++++ b/llvm/lib/Target/RISCV/RISCVFeatures.td +@@ -771,6 +771,9 @@ def HasStdExtSvinval : Predicate<"Subtarget->hasStdExtSvinval()">, + AssemblerPredicate<(all_of FeatureStdExtSvinval), + "'Svinval' (Fine-Grained Address-Translation Cache Invalidation)">; + ++// RISCVFeatures.td - riscv_features - INSERTION_START ++// RISCVFeatures.td - riscv_features - INSERTION_END ++ + def FeatureStdExtSvnapot + : SubtargetFeature<"svnapot", "HasStdExtSvnapot", "true", + "'Svnapot' (NAPOT Translation Contiguity)">; +diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp ++++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +@@ -640,6 +640,9 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, + + setBooleanContents(ZeroOrOneBooleanContent); + ++// RISCVISelLowering.cpp - legal_ops - INSERTION_START ++// RISCVISelLowering.cpp - legal_ops - INSERTION_END ++ + if (Subtarget.hasVInstructions()) { + setBooleanVectorContents(ZeroOrOneBooleanContent); + +diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td +--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td ++++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td +@@ -309,6 +309,9 @@ def simm21_lsb0_jal : Operand { + let OperandType = "OPERAND_PCREL"; + } + ++// RISCVInstrInfo.td - field_types - INSERTION_START ++// RISCVInstrInfo.td - field_types - INSERTION_END ++ + def BareSymbol : AsmOperandClass { + let Name = "BareSymbol"; + let RenderMethod = "addImmOperands"; +@@ -394,6 +397,8 @@ def uimm6gt32 : ImmLeaf; + def AddrRegImm : ComplexPattern; ++// RISCVInstrInfo.td - complex_patterns - INSERTION_START ++// RISCVInstrInfo.td - complex_patterns - INSERTION_END + + // Return the negation of an immediate value. + def NegImm : SDNodeXForm; + def FRM : RISCVReg<0, "frm">; + ++// RISCVRegisterInfo.td - riscv_register_info - INSERTION_START ++// RISCVRegisterInfo.td - riscv_register_info - INSERTION_END ++ + // Shadow Stack register + def SSP : RISCVReg<0, "ssp">; diff --git a/seal5/resources/resources.py b/seal5/resources/resources.py index a7bc634f..57fb4c5d 100644 --- a/seal5/resources/resources.py +++ b/seal5/resources/resources.py @@ -43,3 +43,9 @@ def get_patches(patch_name: Optional[str] = None, target: Optional[str] = None, assert allow_empty, "No patches found!" return ret + + +def get_test_cfg(): + ret = importlib_resources.files("seal5.resources").joinpath("lit.cfg.py") + assert ret.is_file(), f"Test cfg not found: {ret}" + return ret diff --git a/seal5/settings.py b/seal5/settings.py index db2b09bd..2e001942 100644 --- a/seal5/settings.py +++ b/seal5/settings.py @@ -72,11 +72,20 @@ "drop": [], }, }, - "transform": { - "passes": "*", + # "transform": { + # "passes": "*", + # }, + "passes": { + "defaults": { + "skip": [], + "only": [], + "overrides": {}, + }, + "per_model": {}, }, "test": { - "paths": ["MC/RISCV", "CodeGen/RISCV"], + "paths": [], + # "paths": ["MC/RISCV", "CodeGen/RISCV"], }, "llvm": { "state": {"version": "auto", "base_commit": "unknown"}, @@ -127,6 +136,11 @@ "groups": { "all": "*", }, + "tools": { + "pattern_gen": { + "integrated": True, + }, + }, } @@ -140,7 +154,7 @@ def check_supported_types(data): check_supported_types(x) else: if data is not None: - assert isinstance(data, ALLOWED_TYPES), f"Unsupported type: {type(value)}" + assert isinstance(data, ALLOWED_TYPES), f"Unsupported type: {type(data)}" class YAMLSettings: @@ -216,7 +230,9 @@ def merge(self, other: "YAMLSettings", overwrite: bool = False): elif isinstance(v1, list): if overwrite: v2.clear() - v2.extend(v1) + # duplicates are dropped here + new = [x for x in v1 if x not in v2] + v2.extend(new) else: assert isinstance( v2, (int, float, str, bool, Path) @@ -304,9 +320,22 @@ class PatchSettings(YAMLSettings): # self._file = value +# @dataclass +# class TransformSettings(YAMLSettings): +# pass + + @dataclass -class TransformSettings(YAMLSettings): - pass +class PassesSetting(YAMLSettings): + skip: Optional[List[str]] = None + only: Optional[List[str]] = None + overrides: Optional[dict] = None + + +@dataclass +class PassesSettings(YAMLSettings): + defaults: Optional[PassesSetting] = None + per_model: Optional[Dict[str, PassesSetting]] = None @dataclass @@ -419,10 +448,18 @@ class FilterSettings(YAMLSettings): # TODO: functions +@dataclass +class LLVMVersion(YAMLSettings): + major: Optional[int] = None + minor: Optional[int] = None + patch: Optional[int] = None + rc: Optional[int] = None + + @dataclass class LLVMState(YAMLSettings): base_commit: Optional[str] = None - version: Optional[str] = None + version: Optional[Union[str, LLVMVersion]] = None @dataclass @@ -436,33 +473,130 @@ class LLVMSettings(YAMLSettings): configs: Optional[Dict[str, LLVMConfig]] = None +@dataclass +class RISCVLegalizerSetting(YAMLSettings): + name: Optional[Union[str, List[str]]] = None + types: Optional[Union[str, List[str]]] = None + onlyif: Optional[Union[str, List[str]]] = None + + +@dataclass +class RISCVLegalizerSettings(YAMLSettings): + ops: Optional[List[RISCVLegalizerSetting]] = None + + @dataclass class RISCVSettings(YAMLSettings): xlen: Optional[int] = None features: Optional[List[str]] = None # Used for baseline extensions, tune and legalizer + pattern gen # default: zicsr,m,(32/64bit),fast-unaligned-access - # others: zmmul,a,f,d,zfh,zfinx,zdinx,c,zba,zbb,zbc,zbs,zca,zcb,zcd,zcmp,zce,e,no-optimized-zero-stride-load,no-default-unroll,... + # others: zmmul,a,f,d,zfh,zfinx,zdinx,c,zba,zbb,zbc,zbs,zca,zcb,zcd + # zcmp,zce,e,no-optimized-zero-stride-load,no-default-unroll,... transform_info: Optional[Dict[str, Optional[Union[bool, int]]]] = None # options: see ttiimpl_notes.txt # TODO: processor/pipeline/mcpu/tune -> ProcessorSettings + legalization: Optional[Dict[str, RISCVLegalizerSettings]] = None + + +@dataclass +class PatternGenSettings(YAMLSettings): + integrated: Optional[bool] = None + + +@dataclass +class ToolsSettings(YAMLSettings): + pattern_gen: Optional[PatternGenSettings] = None @dataclass class Seal5Settings(YAMLSettings): + directory: Optional[str] = None logging: Optional[LoggingSettings] = None filter: Optional[FilterSettings] = None llvm: Optional[LLVMSettings] = None git: Optional[GitSettings] = None patches: Optional[List[PatchSettings]] = None - transform: Optional[TransformSettings] = None # TODO: make list? + # transform: Optional[TransformSettings] = None # TODO: make list? + passes: Optional[PassesSettings] = None # TODO: make list? test: Optional[TestSettings] = None extensions: Optional[Dict[str, ExtensionsSettings]] = None groups: Optional[GroupsSettings] = None # TODO: make list? inputs: Optional[List[str]] = None riscv: Optional[RISCVSettings] = None + tools: Optional[ToolsSettings] = None + metrics: list = field(default_factory=list) def reset(self): self.extensions = {} self.patches = [] self.inputs = [] + self.metrics = [] + # TODO: clear user provided tests! + + def save(self, dest: Optional[Path] = None): + if dest is None: + dest = self.settings_file + self.to_yaml_file(dest) + + def add_patch(self, patch_settings: PatchSettings): + for ps in self.patches: + if ps.name == patch_settings.name: + raise RuntimeError(f"Duplicate patch '{ps.name}'. Either clean patches or rename patch.") + self.patches.append(patch_settings) + + @property + def model_names(self): + return [Path(path).stem for path in self.inputs] + + @property + def meta_dir(self): + return Path(self.directory) / ".seal5" + + @property + def settings_file(self): + return self.meta_dir / "settings.yml" + + @property + def deps_dir(self): + return self.meta_dir / "deps" + + @property + def build_dir(self): + return self.meta_dir / "build" + + @property + def install_dir(self): + return self.meta_dir / "install" + + @property + def logs_dir(self): + return self.meta_dir / "logs" + + @property + def models_dir(self): + return self.meta_dir / "models" + + @property + def inputs_dir(self): + return self.meta_dir / "inputs" + + @property + def temp_dir(self): + return self.meta_dir / "temp" + + @property + def gen_dir(self): + return self.meta_dir / "gen" + + @property + def tests_dir(self): + return self.meta_dir / "tests" + + @property + def patches_dir(self): # TODO: maybe merge with gen_dir + return self.meta_dir / "patches" + + @property + def log_file_path(self): + return self.logs_dir / "seal5.log" diff --git a/seal5/tools/cdsl2llvm.py b/seal5/tools/cdsl2llvm.py index 9b26e6dc..4051afe0 100644 --- a/seal5/tools/cdsl2llvm.py +++ b/seal5/tools/cdsl2llvm.py @@ -20,6 +20,9 @@ from pathlib import Path from seal5.logging import get_logger +from seal5.settings import PatchSettings +from seal5.types import PatchStage +from seal5.index import File, Directory, NamedPatch, write_index_yaml from seal5 import utils logger = get_logger() @@ -43,6 +46,61 @@ def build_pattern_gen( ) +def get_pattern_gen_patches( + src: Path, + temp_dir: Path, + verbose: bool = False, +): + # TODO: copy! + artifacts = [] + directory_artifact = Directory( + "llvm/tools/pattern-gen", + src_path=f"{src}/llvm/tools/pattern-gen", + ) + artifacts.append(directory_artifact) + file_artifact = File( + "llvm/include/llvm/CodeGen/GlobalISel/PatternGen.h", + src_path=f"{src}/llvm/include/llvm/CodeGen/GlobalISel/PatternGen.h", + ) + artifacts.append(file_artifact) + file_artifact = File( + "llvm/lib/CodeGen/GlobalISel/PatternGen.cpp", + src_path=f"{src}/llvm/lib/CodeGen/GlobalISel/PatternGen.cpp", + ) + artifacts.append(file_artifact) + patch_artifact = NamedPatch( + "llvm/include/llvm/InitializePasses.h", + key="initialize_passes_decl", + content="void initializePatternGenPass(PassRegistry&);", + ) + artifacts.append(patch_artifact) + patch_artifact = NamedPatch( + "llvm/lib/CodeGen/GlobalISel/CMakeLists.txt", + key="gisel_cmake_srcs", + content=" PatternGen.cpp", + ) + artifacts.append(patch_artifact) + patch_artifact = NamedPatch( + "llvm/lib/CodeGen/GlobalISel/GlobalISel.cpp", + key="gisel_init", + content=" initializePatternGenPass(Registry);", + ) + artifacts.append(patch_artifact) + + index_file = temp_dir / "pattern_gen_support_index.yml" + write_index_yaml(index_file, artifacts, {}, content=True) + patch_settings = PatchSettings( + name="pattern_gen_support", + stage=int(PatchStage.PHASE_2), + comment="Integrate PatternGen in Seal5 LLVM", + index=str(index_file), + generated=True, + target="llvm", + ) + + return patch_settings + + def build_llc( src: Path, dest: Path, debug: bool = False, use_ninja: bool = False, verbose: bool = False, cmake_options: dict = {} ): @@ -71,6 +129,8 @@ def run_pattern_gen( skip_verify=True, debug=False, ): + if not isinstance(build_dir, Path): + build_dir = Path(build_dir) pattern_gen_args = [src] if dest: @@ -83,7 +143,7 @@ def run_pattern_gen( attrs = ["+m", "+fast-unaligned-access"] if ext: ext_ = ext.lower() - ext_ = ext_.replace("std", "").replace("vendor", "") + ext_ = ext_.replace("std", "").replace("vendor", "").replace("ext", "") attrs.append(f"+{ext_}") mattr = ",".join(attrs) @@ -193,6 +253,9 @@ def convert_ll_to_gmir( ): llc_args = [src, "-mtriple=riscv32-unknown-elf", "-stop-after=irtranslator", "-global-isel", "-O3"] + if not isinstance(build_dir, Path): + build_dir = Path(build_dir) + assert dest is not None llc_args.extend(["-o", str(dest)]) diff --git a/seal5/tools/inject_patches.py b/seal5/tools/inject_patches.py index e3b0f7fb..b31fa2ae 100644 --- a/seal5/tools/inject_patches.py +++ b/seal5/tools/inject_patches.py @@ -104,6 +104,7 @@ def generate_patch_fragment(artifact): content_ = artifact.get("content", None) assert dest_path is not None is_file = False + is_dir = False is_patch = False # TODO: do not depend on dest_path if key: # NamedPatch @@ -112,15 +113,47 @@ def generate_patch_fragment(artifact): raise NotImplementedError elif line is not None: raise NotImplementedError - else: # File - is_file = True + else: # File/Dir + if content_ is None: + if Path(src_path).is_file(): + is_file = True + elif Path(src_path).is_dir(): + is_dir = True + else: + raise RuntimeError(f"File not found: {src_path}") + else: + is_file = True if content_: assert isinstance(content_, str) else: - assert src_path is not None - assert Path(src_path).is_file(), f"File not found: {src_path}" - with open(src_path, "r") as f: - content_ = f.read() + if is_patch or is_file: + with open(src_path, "r") as f: + content_ = f.read() + elif is_dir: + files = Path(src_path).rglob("*") + base = dest_path + # print("base", base) + # print("files", files) + fragments = [] + for file in files: + if file.is_dir(): + continue + # print("file", file) + dest_path_ = str(Path(base) / (str(file).replace(f"{src_path}/", ""))) + # print("dest_path_", dest_path_) + src_path_ = file + file_artifact = { + "dest_path": str(dest_path_), + "src_path": str(src_path_), + } + fragment = generate_patch_fragment(file_artifact) + fragments.append(fragment) + # print("fragments", fragments) + # input("AAA") + return "".join(fragments) + # raise NotImplementedError + else: + assert False content = "+" + content_.replace("\n", "\n+") if is_patch: # Updating existing file diff --git a/seal5/tools/llvm.py b/seal5/tools/llvm.py index 49e17667..0869d808 100644 --- a/seal5/tools/llvm.py +++ b/seal5/tools/llvm.py @@ -18,6 +18,7 @@ # """LLVM utils for seal5.""" import re +import os from pathlib import Path from typing import List, Optional @@ -53,6 +54,8 @@ def clone_llvm_repo( dest: Path, clone_url: str, ref: Optional[str] = None, refresh: bool = False, label: str = "default" ): sha = None + version_info = {} + repo = None if dest.is_dir(): if refresh: logger.debug("Refreshing LLVM repository: %s", dest) @@ -62,22 +65,37 @@ def clone_llvm_repo( if ref: repo.git.checkout(ref) repo.git.pull("origin", ref) - repo.create_tag(f"seal5-{label}-base", "-f") - sha = repo.head.commit.hexsha else: logger.debug("Cloning LLVM repository: %s", clone_url) repo = git.Repo.clone_from(clone_url, dest, no_checkout=ref is not None) if ref: logger.debug("Checking out branch: %s", ref) repo.git.checkout(ref) - repo.create_tag(f"seal5-{label}-base", "-f") - sha = repo.head.commit.hexsha - return sha + repo.create_tag(f"seal5-{label}-base", "-f") + # git describe --tags --match "llvmorg-[0-9]*.[0-9]*.[0-9]*" + describe = repo.git.describe("--tags", "--match", "llvmorg-[0-9]*.[0-9]*.[0-9]*") + if describe: + splitted = describe.split("-", 3) + base = splitted[0] + assert base == "llvmorg" + version = splitted[1] + major, minor, patch = version.split(".") + version_info["major"] = int(major) + version_info["minor"] = int(minor) + version_info["patch"] = int(patch) + rest = splitted[2] + if "rc" in rest: + rc = rest.split("-", 1)[0][2:] + version_info["rc"] = int(rc) + + sha = repo.head.commit.hexsha + return sha, version_info def build_llvm( src: Path, dest: Path, + target: str = "all", debug: Optional[bool] = None, use_ninja: Optional[bool] = None, verbose: bool = False, @@ -94,10 +112,13 @@ def build_llvm( print_func=logger.info if verbose else logger.debug, live=True, ) - utils.make(cwd=dest, print_func=logger.info if verbose else logger.debug, live=True) + utils.make(target=target, cwd=dest, print_func=logger.info if verbose else logger.debug, live=True) def test_llvm(base: Path, build_dir: Path, test_paths: List[str] = [], verbose: bool = False): + env = os.environ.copy() + old_path = env["PATH"] + env["PATH"] = f"{build_dir}/bin:{old_path}" lit_exe = build_dir / "bin" / "llvm-lit" failing_tests = [] for test_path in test_paths: @@ -110,6 +131,7 @@ def handler(code, out): base / test_path, print_func=logger.info if verbose else logger.debug, live=True, + env=env, handle_exit=handler, ) failing = re.compile(r"FAIL: LLVM :: (.*) \(").findall(out) diff --git a/seal5/transform/detect_inouts/visitor.py b/seal5/transform/detect_inouts/visitor.py index ddfad266..82438c60 100644 --- a/seal5/transform/detect_inouts/visitor.py +++ b/seal5/transform/detect_inouts/visitor.py @@ -8,7 +8,7 @@ """TODO""" -from m2isar.metamodel import arch, behav +from m2isar.metamodel import behav # pylint: disable=unused-argument diff --git a/seal5/transform/detect_side_effects/visitor.py b/seal5/transform/detect_side_effects/visitor.py index 2ea077a7..82259725 100644 --- a/seal5/transform/detect_side_effects/visitor.py +++ b/seal5/transform/detect_side_effects/visitor.py @@ -8,7 +8,6 @@ """TODO""" -from m2isar import flatten from m2isar.metamodel import arch, behav # pylint: disable=unused-argument diff --git a/seal5/transform/eliminate_mod_rfs/visitor.py b/seal5/transform/eliminate_mod_rfs/visitor.py index b9b8a1d2..60ff4e69 100644 --- a/seal5/transform/eliminate_mod_rfs/visitor.py +++ b/seal5/transform/eliminate_mod_rfs/visitor.py @@ -8,7 +8,7 @@ """TODO""" -from m2isar.metamodel import arch, behav +from m2isar.metamodel import behav # pylint: disable=unused-argument diff --git a/seal5/transform/eliminate_rd_cmp_zero/visitor.py b/seal5/transform/eliminate_rd_cmp_zero/visitor.py index 210a51a4..dcf19244 100644 --- a/seal5/transform/eliminate_rd_cmp_zero/visitor.py +++ b/seal5/transform/eliminate_rd_cmp_zero/visitor.py @@ -8,7 +8,7 @@ """TODO""" -from m2isar.metamodel import arch, behav +from m2isar.metamodel import behav # pylint: disable=unused-argument diff --git a/seal5/transform/explicit_truncations/visitor.py b/seal5/transform/explicit_truncations/visitor.py index 37df7dbe..29e63c80 100644 --- a/seal5/transform/explicit_truncations/visitor.py +++ b/seal5/transform/explicit_truncations/visitor.py @@ -20,10 +20,10 @@ type directly to the :class:`IntLiteral` and discard the type conversion """ import logging +from m2isar.metamodel import behav logger = logging.getLogger(__name__) -from m2isar.metamodel import arch, behav # pylint: disable=unused-argument diff --git a/seal5/transform/filter_model/filter.py b/seal5/transform/filter_model/filter.py index cdd1bb6e..e1acbea8 100644 --- a/seal5/transform/filter_model/filter.py +++ b/seal5/transform/filter_model/filter.py @@ -114,10 +114,38 @@ def main(): def opcodes_helper(x): OPCODE_LOOKUP = { + "LOAD": 0b00000, + "LOAD-FP": 0b00001, "custom-0": 0b00010, + "MISC-MEM": 0b00011, + "OP-IMM": 0b00100, + "AUIPC": 0b00101, + "OP-IMM-32": 0b00110, + # "48bit": 0b00111, + "STORE": 0b01000, + "STORE-FP": 0b01001, "custom-1": 0b01010, - "custom-2": 0b10110, + "AMO": 0b01011, + "OP": 0b01100, + "LUI": 0b01101, + "OP-32": 0b01110, + # "64bit": 0b01111, + "MADD": 0b10000, + "MSUB": 0b10001, + "NMADD": 0b10010, + "NMSUB": 0b10011, + "OP-FP": 0b10100, + "OP-V": 0b10101, + "custom-2": 0b10110, # rv128i + # "48bit2": 0b10111, + "BRANCH": 0b11000, + "JALR": 0b11001, + # "reserved": 0b11010, + "JAL": 0b11011, + "SYSTEM": 0b11100, + "OP-P": 0b11101, "custom-3": 0b11110, + # "80bit+": 0b11111, } try: x = int(x, 0) @@ -154,7 +182,6 @@ def opcodes_helper(x): # input("456") def check_filter(name, keep, drop): - # print("check_filter", name, keep, drop) if drop and keep: return name not in drop and name in keep elif keep: @@ -167,7 +194,7 @@ def check_encoding_filter(enc, keep, drop, keep2, drop2): opcode = None size = 0 for e in reversed(enc): - print("e", e, dir(e)) + # print("e", e, dir(e)) if isinstance(e, arch.BitVal): length = e.length if size == 0: @@ -179,15 +206,17 @@ def check_encoding_filter(enc, keep, drop, keep2, drop2): else: assert False size += length - if opcode is None: # not found (not a riscv insn?) - return True - assert size in [16, 32, 64, 128] + assert size in [16, 32, 64, 128], f"Invalid size: {size}" if drop2 and keep2: - return size not in drop2 and size in keep2 + ret = size not in drop2 and size in keep2 elif keep2: - return size in keep2 + ret = size in keep2 elif drop2: - return size not in drop2 + ret = size not in drop2 + if not ret: + return False + if opcode is None: # not found (not a riscv insn?) + return True if drop and keep: return opcode not in drop and opcode in keep elif keep: diff --git a/seal5/transform/infer_types/visitor.py b/seal5/transform/infer_types/visitor.py index 8f3ffbcb..c492fbfa 100644 --- a/seal5/transform/infer_types/visitor.py +++ b/seal5/transform/infer_types/visitor.py @@ -20,13 +20,12 @@ type directly to the :class:`IntLiteral` and discard the type conversion """ import logging - -logger = logging.getLogger(__name__) - from copy import copy from m2isar.metamodel import arch, behav +logger = logging.getLogger(__name__) + # pylint: disable=unused-argument @@ -151,12 +150,12 @@ def slice_operation(self: behav.SliceOperation, context): if not isinstance(self.left, behav.IntLiteral): logger.warning("Can not infer type of non-static slice operation. Skipping...") return self - l = self.left.value + lval = self.left.value if not isinstance(self.right, behav.IntLiteral): logger.warning("Can not infer type of non-static slice operation. Skipping...") return self - r = self.right.value - width = l - r + 1 if l > r else r - l + 1 + rval = self.right.value + width = lval - rval + 1 if lval > rval else rval - lval + 1 ty_ = copy(ty) ty_._width = width self.inferred_type = ty_ diff --git a/seal5/transform/process_settings/transform.py b/seal5/transform/process_settings/transform.py index dad6e239..e761a928 100644 --- a/seal5/transform/process_settings/transform.py +++ b/seal5/transform/process_settings/transform.py @@ -14,7 +14,7 @@ import pickle from typing import Union -from m2isar.metamodel import arch, patch_model +from m2isar.metamodel import arch from seal5.settings import Seal5Settings, ExtensionsSettings @@ -73,6 +73,9 @@ def main(): else: assert False + assert "settings" not in model + model["settings"] = settings + for set_name, set_def in model["sets"].items(): ext_settings = settings.extensions.get(set_name, None) if ext_settings is None: diff --git a/seal5/transform/simplify_trivial_slices/visitor.py b/seal5/transform/simplify_trivial_slices/visitor.py index aaf54768..b9f1c9fa 100644 --- a/seal5/transform/simplify_trivial_slices/visitor.py +++ b/seal5/transform/simplify_trivial_slices/visitor.py @@ -20,10 +20,10 @@ type directly to the :class:`IntLiteral` and discard the type conversion """ import logging +from m2isar.metamodel import arch, behav logger = logging.getLogger(__name__) -from m2isar.metamodel import arch, behav # pylint: disable=unused-argument @@ -77,7 +77,8 @@ def slice_operation(self: behav.SliceOperation, context): return behav.Group(self.expr) # if self.right.value == 0: - # return behav.TypeConv(arch.DataType.S if self.expr.inferred_type.signed else arch.DataType.U, target_width, self.expr) + # return behav.TypeConv(arch.DataType.S if self.expr.inferred_type.signed + # else arch.DataType.U, target_width, self.expr) return self diff --git a/seal5/types.py b/seal5/types.py index 0fbaa454..6f9460ed 100644 --- a/seal5/types.py +++ b/seal5/types.py @@ -32,3 +32,4 @@ class PatchStage(IntEnum): PHASE_2 = 2 PHASE_3 = 3 PHASE_4 = 4 + PHASE_5 = 5 diff --git a/seal5/utils.py b/seal5/utils.py index aab1100c..d8f34617 100644 --- a/seal5/utils.py +++ b/seal5/utils.py @@ -154,13 +154,15 @@ def cmake(src, *args, debug: Optional[bool] = None, use_ninja: Optional[bool] = return exec_getout(*cmd, cwd=cwd, **kwargs) -def make(*args, threads=multiprocessing.cpu_count(), use_ninja=False, cwd=None, verbose=False, **kwargs): +def make(*args, target=None, threads=multiprocessing.cpu_count(), use_ninja=False, cwd=None, verbose=False, **kwargs): if cwd is None: raise RuntimeError("Please always pass a cwd to make()") if isinstance(cwd, Path): cwd = str(cwd.resolve()) # TODO: make sure that ninja is installed? extraArgs = [] + if target: + extraArgs.append(target) tool = "ninja" if use_ninja else "make" extraArgs.append("-j" + str(threads)) cmd = [tool] + extraArgs + list(args) @@ -173,7 +175,21 @@ def python(*args, **kwargs): def clean_path(path: Path, interactive: bool = False): - raise NotImplementedError + if interactive: + answer = input(f"Remove '{path}' [Y|n]") + if len(answer) == 0: + answer = "Y" + if answer.lower() in ["j", "y"]: + allow = True + else: + allow = False + else: + allow = True + if not path.is_dir(): + # TODO: warning + return + if allow: + shutil.rmtree(path) def merge_dicts(a: dict, b: dict, path=[]):