From 32ef0682238d268e8b6613926aebc0aaaf41396d Mon Sep 17 00:00:00 2001 From: Brendan Punsky Date: Sat, 29 Aug 2026 00:23:24 -0400 Subject: [PATCH] rexcode/arm32: the width and saturate fields hold one less than they name SSAT, SSAT16, USAT and USAT16 encode their saturate position minus one, and SBFX and UBFX their width minus one, so a field of zero means one. All of them printed the raw field, which is not a value the instruction can take -- `ssat r0, #0, r0` is not assemblable. BFI and BFC are different again: their field is the top bit's position, and the width the syntax wants is msb - lsb + 1. They shared an encoding with SBFX and UBFX, which need the opposite arithmetic, so they now have their own. Packing an msb needs the lsb from a sibling operand, so the packer takes the instruction rather than one operand in isolation. Also worth recording: the sweep had been running llvm-mc with `-mattr=+all`, which that target does not recognise and silently ignores, so every CRC32, FP16, v8.1a and dot-product entry looked like a reserved encoding. With the features actually enabled, 815 of 1139 A32 entries round-trip, and 38 rather than 143 are genuinely reserved. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_018UmHLRF11EoWwNWCJ7JGaA --- core/rexcode/isa/arm32/decoder.odin | 11 +++++++-- core/rexcode/isa/arm32/encoder.odin | 22 ++++++++++++------ core/rexcode/isa/arm32/encoding_types.odin | 3 +++ .../isa/arm32/tablegen/encoding_table.odin | 8 +++---- .../tablegen/generated/decode_tables.odin | 8 +++---- .../tablegen/generated/encode_tables.odin | 8 +++---- .../isa/arm32/tables/arm32.encode_forms.bin | Bin 37927 -> 37927 bytes .../isa/arm32/tables/arm32.entries.bin | Bin 37927 -> 37927 bytes 8 files changed, 39 insertions(+), 21 deletions(-) diff --git a/core/rexcode/isa/arm32/decoder.odin b/core/rexcode/isa/arm32/decoder.odin index 633ad28e6..7e7c740f1 100644 --- a/core/rexcode/isa/arm32/decoder.odin +++ b/core/rexcode/isa/arm32/decoder.odin @@ -529,8 +529,15 @@ unpack_operand :: proc(word: u32, enc: Operand_Encoding, ot: Operand_Type) -> Op return op_imm(i64((word >> 18) & 0xF)) // ---- Saturate / bit field ---- - case .SAT_IMM5, .SAT_IMM5_T32, .BFI_MSB: - return op_imm(i64((word >> 16) & 0x1F)) + case .SAT_IMM5, .SAT_IMM5_T32: + // The field holds one less than the saturate position it names. + return op_imm(i64(((word >> 16) & 0x1F) + 1)) + case .BFX_WIDTH: + // One less than the width. + return op_imm(i64(((word >> 16) & 0x1F) + 1)) + case .BFI_MSB: + // The top bit's position; the syntax wants the width. + return op_imm(i64(((word >> 16) & 0x1F) - ((word >> 7) & 0x1F) + 1)) case .BFI_LSB, .BFI_LSB_T32: return op_imm(i64((word >> 7) & 0x1F)) case .NEON_SHIFT_IMM6: diff --git a/core/rexcode/isa/arm32/encoder.odin b/core/rexcode/isa/arm32/encoder.odin index d5b8a68b1..666266a27 100644 --- a/core/rexcode/isa/arm32/encoder.odin +++ b/core/rexcode/isa/arm32/encoder.odin @@ -202,10 +202,10 @@ encode_one_inline :: #force_inline proc( word = (word & 0x0FFFFFFF) | (u32(inst.cond) << 28) } - if form.enc[0] != .NONE { word |= pack_operand_inline(&inst.ops[0], form.enc[0], pc, inst_idx, relocs, form) } - if form.enc[1] != .NONE { word |= pack_operand_inline(&inst.ops[1], form.enc[1], pc, inst_idx, relocs, form) } - if form.enc[2] != .NONE { word |= pack_operand_inline(&inst.ops[2], form.enc[2], pc, inst_idx, relocs, form) } - if form.enc[3] != .NONE { word |= pack_operand_inline(&inst.ops[3], form.enc[3], pc, inst_idx, relocs, form) } + if form.enc[0] != .NONE { word |= pack_operand_inline(&inst.ops[0], form.enc[0], pc, inst_idx, relocs, form, inst) } + if form.enc[1] != .NONE { word |= pack_operand_inline(&inst.ops[1], form.enc[1], pc, inst_idx, relocs, form, inst) } + if form.enc[2] != .NONE { word |= pack_operand_inline(&inst.ops[2], form.enc[2], pc, inst_idx, relocs, form, inst) } + if form.enc[3] != .NONE { word |= pack_operand_inline(&inst.ops[3], form.enc[3], pc, inst_idx, relocs, form, inst) } return word, inst_size_from_bits(form.bits, form.mode), true } @@ -334,6 +334,8 @@ pack_operand_inline :: #force_inline proc( inst_idx: u16, relocs: ^[dynamic]Relocation, form: ^Encoding, + // BFI's msb is lsb + width - 1, so packing it needs a sibling operand. + inst: ^Instruction, ) -> u32 { switch enc { case .NONE, .IMPL: @@ -631,9 +633,15 @@ pack_operand_inline :: #force_inline proc( case .IT_MASK: return u32(op.immediate) & 0xFF case .CPS_IFLAGS: return u32(op.immediate) & 0x1FF case .HINT_FIELD: return u32(op.immediate) & 0xFF - case .SAT_IMM5, .SAT_IMM5_T32: - return (u32(op.immediate) & 0x1F) << 16 - case .BFI_MSB: return (u32(op.immediate) & 0x1F) << 16 + case .SAT_IMM5, .SAT_IMM5_T32, .BFX_WIDTH: + return ((u32(op.immediate) - 1) & 0x1F) << 16 + case .BFI_MSB: + // msb = lsb + width - 1; the lsb rides in whichever slot carries it. + lsb: u32 = 0 + for e, k in form.enc { + if e == .BFI_LSB || e == .BFI_LSB_T32 { lsb = u32(inst.ops[k].immediate); break } + } + return ((lsb + u32(op.immediate) - 1) & 0x1F) << 16 case .BFI_LSB, .BFI_LSB_T32: return (u32(op.immediate) & 0x1F) << 7 case .NEON_SHIFT_IMM6: return (u32(op.immediate) & 0x3F) << 16 diff --git a/core/rexcode/isa/arm32/encoding_types.odin b/core/rexcode/isa/arm32/encoding_types.odin index d600b6a88..82643db9d 100644 --- a/core/rexcode/isa/arm32/encoding_types.odin +++ b/core/rexcode/isa/arm32/encoding_types.odin @@ -400,6 +400,9 @@ Operand_Encoding :: enum u8 { SAT_IMM5_T32, // Thumb-2 saturate amount // ---- BFC/BFI/SBFX/UBFX ---- + // SBFX/UBFX hold the width less one, where BFI/BFC hold the top bit's + // position and the width is msb - lsb + 1. + BFX_WIDTH, BFI_MSB, // bits 20-16 (msb position) BFI_LSB, // bits 11-7 (lsb position; also shift_imm slot) BFI_LSB_T32, // Thumb-2 BFI lsb (different layout) diff --git a/core/rexcode/isa/arm32/tablegen/encoding_table.odin b/core/rexcode/isa/arm32/tablegen/encoding_table.odin index 1f67e57ea..ec127ce1a 100644 --- a/core/rexcode/isa/arm32/tablegen/encoding_table.odin +++ b/core/rexcode/isa/arm32/tablegen/encoding_table.odin @@ -754,14 +754,14 @@ ENCODING_TABLE := #partial [Mnemonic][]Encoding{ {.BFI, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFI_MSB}, 0xF3600000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}}, }, .SBFX = { - {.SBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD, .RM_A32, .BFI_LSB, .BFI_MSB}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {}}, + {.SBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD, .RM_A32, .BFI_LSB, .BFX_WIDTH}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {}}, // T32 SBFX - {.SBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFI_MSB}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}}, + {.SBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFX_WIDTH}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}}, }, .UBFX = { - {.UBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD, .RM_A32, .BFI_LSB, .BFI_MSB}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {}}, + {.UBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD, .RM_A32, .BFI_LSB, .BFX_WIDTH}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {}}, // T32 UBFX - {.UBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFI_MSB}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}}, + {.UBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFX_WIDTH}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}}, }, // ========================================================================= diff --git a/core/rexcode/isa/arm32/tablegen/generated/decode_tables.odin b/core/rexcode/isa/arm32/tablegen/generated/decode_tables.odin index f7f3870a1..fbf32d8d7 100644 --- a/core/rexcode/isa/arm32/tablegen/generated/decode_tables.odin +++ b/core/rexcode/isa/arm32/tablegen/generated/decode_tables.odin @@ -979,10 +979,10 @@ DECODE_ENTRIES := [1649]lib.Decode_Entry{ { .LDRB, {.GPR,.MEM,.NONE,.NONE}, {.RT_A32,.MEM_REG_OFFSET,.NONE,.NONE}, 0x07500000, 0x0F700010, .BASE, .A32, {}, {.NONE,.NONE} }, { .USAD8, {.GPR,.GPR,.GPR,.NONE}, {.RN_A32,.RM_A32,.RS_A32,.NONE}, 0x0780F010, 0x0FF0F0F0, .V6, .A32, {}, {.NONE,.NONE} }, { .USADA8, {.GPR,.GPR,.GPR,.GPR}, {.RN_A32,.RM_A32,.RS_A32,.RD}, 0x07800010, 0x0FF000F0, .V6, .A32, {}, {.NONE,.NONE} }, - { .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, + { .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFX_WIDTH}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, { .BFC, {.GPR,.IMM5,.IMM5_W,.NONE}, {.RD,.BFI_LSB,.BFI_MSB,.NONE}, 0x07C0001F, 0x0FE0007F, .V6T2, .A32, {}, {.NONE,.NONE} }, { .BFI, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07C00010, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, - { .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, + { .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFX_WIDTH}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, { .UDF, {.IMM,.NONE,.NONE,.NONE}, {.NONE,.NONE,.NONE,.NONE}, 0xE7F000F0, 0xFFF000F0, .BASE, .A32, {}, {.NONE,.NONE} }, { .STMDA, {.GPR,.GPR_LIST,.NONE,.NONE}, {.RN_A32,.A32_REG_LIST,.NONE,.NONE}, 0x08000000, 0x0FD00000, .BASE, .A32, {}, {.NONE,.NONE} }, { .RFE, {.GPR,.NONE,.NONE,.NONE}, {.RN_A32,.NONE,.NONE,.NONE}, 0xF8100A00, 0xFE10FFFF, .V6, .A32, {}, {.NONE,.NONE} }, @@ -1407,8 +1407,8 @@ DECODE_ENTRIES := [1649]lib.Decode_Entry{ { .USAT16, {.GPR,.IMM4_SAT,.GPR,.NONE}, {.RD_T32,.SAT_IMM5_T32,.RN_T32,.NONE}, 0xF3A00000, 0xFFF0F0F0, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, { .BFC, {.GPR,.IMM5,.IMM5_W,.NONE}, {.RD_T32,.BFI_LSB_T32,.BFI_MSB,.NONE}, 0xF36F0000, 0xFFFF8000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, { .BFI, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3600000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, - { .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, - { .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, + { .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFX_WIDTH}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, + { .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFX_WIDTH}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, { .SSAT, {.GPR,.IMM4_SAT,.GPR_SHIFTED,.NONE}, {.RD_T32,.SAT_IMM5_T32,.RN_T32,.NONE}, 0xF3000000, 0xFFD08020, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, { .USAT, {.GPR,.IMM4_SAT,.GPR_SHIFTED,.NONE}, {.RD_T32,.SAT_IMM5_T32,.RN_T32,.NONE}, 0xF3800000, 0xFFD08020, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, { .MOVW, {.GPR,.IMM16_LO_HI,.NONE,.NONE}, {.RD_T32,.NONE,.NONE,.NONE}, 0xF2400000, 0xFBF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, diff --git a/core/rexcode/isa/arm32/tablegen/generated/encode_tables.odin b/core/rexcode/isa/arm32/tablegen/generated/encode_tables.odin index 9bcca5dae..7b0e2b271 100644 --- a/core/rexcode/isa/arm32/tablegen/generated/encode_tables.odin +++ b/core/rexcode/isa/arm32/tablegen/generated/encode_tables.odin @@ -209,11 +209,11 @@ ENCODE_FORMS := [1649]lib.Encoding{ { .BFI, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07C00010, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, { .BFI, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3600000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, // .SBFX - { .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, - { .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, + { .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFX_WIDTH}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, + { .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFX_WIDTH}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, // .UBFX - { .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, - { .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, + { .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFX_WIDTH}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} }, + { .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFX_WIDTH}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} }, // .SXTB { .SXTB, {.GPR,.GPR,.NONE,.NONE}, {.RD,.RM_A32,.NONE,.NONE}, 0x06AF0070, 0x0FFF0070, .V6, .A32, {}, {.NONE,.NONE} }, { .SXTB, {.GPR_LOW,.GPR_LOW,.NONE,.NONE}, {.RD_T16_LO,.RM_T16_LO,.NONE,.NONE}, 0x0000B240, 0x0000FFC0, .V6, .T32, {}, {.NONE,.NONE} }, diff --git a/core/rexcode/isa/arm32/tables/arm32.encode_forms.bin b/core/rexcode/isa/arm32/tables/arm32.encode_forms.bin index c3ad6e7d879195a4272a1d51340cb07fb7813497..510521e939844c6ce2517016f1c9a7dcddcb4517 100644 GIT binary patch delta 1403 zcmZ3!f@%2*rVaA^OeMvW75PP_N{bm781g?eH2nY1&Zxk^AkDzYC@RLpQc^rQfL}Bg zB%Sb?q2U8WRt6?p6u_{6y@25XKRW{h0|P{uo2Rshfx!WxS{6z51B7abFgH)>=0p7D zNfKoY0t^kG1o#{N2{15lGh{MyaS1SJ>6A?lEE1C_V+deqcpu2$@Lrlxftw+Vk&{z^ zL0z+Kay>+nvEfqyOlKA&7ndkl=h~u9kg-fi#^%6{^(z*OEn^5^LNYcNW-NmMGm@TM zm>z}zW+Z#^5UN>`ROi7|Pd-=-a<~8sl4L$y@_lg;$ZhOMk_B+d$`W0WBxl2?3lLv% zF%&U!atSb~>y&NYSJK7^ayhbWDNL3jfTQ8_1^$Nr0t^gX3}tX(0S+Xy%HhHR$ifwj zT$}<7TAF2>7nYslXRMuE&?d%MJGs5hS)!JKfr0S={|Cko|Nl!dsxUAdVN_>eV5pt^ zsBJ-g9d|t__TMouH2jwZC7v^kii`pbP~m@Y;j>7>4G7iekcAPd&oe4AG78jj^VBmi zIJ{$EH~=yG0M!2{YDl^YAh-fSdv~{0g!#LiJT7 zVI;G!L4+qabcjjSser;j;SB?W0mPlx85KDN80ysPCvWHwsfSAcflJ?jNi#4o7$Agi zGAeR#2-K;l)iW?8APl?7sL07FP^Ye0&%jWC5Wa=18ezySWYu6{9*`F&}3kkfH3P0L{ddnje(&7tO=~=4#+G{sA`1pT}ESVF0L8|Z5;-F#(y9B8UIN$ zDsVE~V>D)9V5r%AsI#8Mpq?R5p7C9sd?P5&vN8xTaWM)oXz|uFyrG74z% z^3+c*>=R?GpWM+mU!sh`fxqG1MR{;`XJC+Gg5_xY{soL>lOObhGS)#kBqhplC43V= z`TC$-!@rC24gaM<=}{FXiE6AGLRcQjD0R57BeEkjV8W9dCxEO!fGnv6ms}5%L|Cr_ LliYlFf)_IY(W+a3 delta 1403 zcmZ3!f@%2*rVaA^OvOc$75PP_N{Sd581g?eH2nY1&Zxk^AkDzYC@RLpQd~4SfL}Bg zB%Sb?q2U8WRt7E`z_5V5fZ+i@I|Bm)14Njc2c+5op;{J6^#g=zh%h%#$>u}+Xm}sU-|$|VQGuHwi;n3mL5UGbs|A9;2fJrkjFc=_&Z!#)! za0t|@sMRqrBp?jC$*9Q5DNw7fS;xRofDpcgtQuj+Eo9YTVIHujZ-bO`)K2#A6pO7@ z*U)5On1C?r4n$H#RgHn60jvqE=MKm$PN-^x@Lfh@Z7!~A25lV%e#UqDc4uIaVuIyp`~C%trIR1@gEH1ZIV2^@a3y>bK>7Nh zT*JSM@(ur`LFrKyCW&gS8bVke$tZQWup_b~G+@G$8z+FQKY%Q$1(#e8lSEjr1C!i* IcY+r)0J*1I8~^|S diff --git a/core/rexcode/isa/arm32/tables/arm32.entries.bin b/core/rexcode/isa/arm32/tables/arm32.entries.bin index b6b1772d5e44e8387cf1b8157340c4116f4e0661..c02fc29c065c243f53afcca8a724e118ece423fa 100644 GIT binary patch delta 1420 zcmZ3!f@%2*rVXvi^<@kKoDH8Y@HhMuU|`^4C}HH}5@1l*DPssg7A}Je3veK*E{6*T zAPZM8a&ZbUXla%)2yh{(&Sd1`5@65*na$AfDFAFG4+A$t7EFAyfQpzz8AAXQOqPM0 zAr~%bry>?x#t^`YB$*GBWC&nE5-vapvm*&-GIDYXFsOr^#nA9c0Bk?ldZ_T^jbQ5q zn2;oMVR|OtR}s@MV-R3PlFWxmG6=9B2^YYGH>;=y2uqYP2yirfzQEt`9~{D^urS-) zW3fn%v1GD-xTrx%F@rqA0rq-^2mI^|3=9m?42+DTVoWS0#R3cm*b897vM}MzCE=0c z%ysJZlP~0n#MY_QGcYhHykTH4_%F+-z`$^mQIV5VpiW)0o`InNMVNy_piV`to`E3& zA$*%rk%NPyPDNFXfuR9Kn3I#UZnI;)5R*hbL!Lb2yE^&C_tK0CtPBE7T#Nz?TDKR{&EY$;8Da3X1K?`%1(Z%O+nfSx{fbaFDOz-9`BZkSPod zN-!l12j!52RpG)8{7AxT2w{07VRg8$BeH4@gfLt+j{*aOCQN)%nHXc)X7REujQV8^ z0SpcA1Np(uf}}^V%NYU~k%V&?VIKNbu3cZp%~Q|7;P8%t;Q%DeFET1JG73P14Edv7s z;{pB;j3560mts_5U^v34&cMJ>J2|jYoUwLtX5~}H+Rgq|D$MSs#S9D#`JWjY{{LqO zB@jsZ;^qN~CwyjT_y7@>0jDo+p3)))1_y*NB-wHEly2Tt+rU*{rw($S!5?^XyTPc) zA;17lg$UtW$f*!T7?BDQ!goNbIqTFlG#MBspa>%pCs=s$&JJ$In$1T$gqS423F_ZP z`G)_}AaAL_B6srpE>KBwfWHADsRNgk?QUnRnY^_794r?9!DI0fC|+Q(h!DO45{AVh fLiiez@BxJI8Kmf9_y>>0b4bDsVByW5dQ8~?TD@S! delta 1420 zcmZ3!f@%2*rVXvi^`#5~oDH8Y@HhMuU|`^4C}HH}5@1l*DP;&i7A}Je3veK*E{6*T zAPZM8a&ZbUXla%*2yh{(&Sd1`5@65*na$AfDFAFG4+A$t7EFAyfQpzzDMJ7gOqPM0 zAr~%bry>?x$`HVcB$*GBWC&nE5-vapvm*&-GIDYXFsOr^#nA9c0Bk?ldZ_T^jbQ5q zn2;oMVR|OtR}s@MWe{LRlFWxmG6=9B2^YYGH>;=y2uqYQ2yirfzQEt`9~{D^urS-) zW3fn%v3Rn6xTryK5raI#0rq-^2mI^|3=9m?42+DTVoWT>MFI>5*b897vM}MzCE=0c z%(d!ulP~0n#MY|RF)%PFykTH4_%F+-z`$^mQIV5VpjKV8j)9>7MVNy_pjJh#j)5To zA$*%rk%NPyRz+2ffuR9Kn3I#UcC%x?5R*h5L!Lb2yE^&C_tK0CtPBE7T#Nz?TD*0e zvkO(38S5rbED;m0OW4FdxM z;{pB;j3560mts_5U^v34&cMJ>GdZwQoUvwdX5~}Hn$7-ID$MRBMGOoK`JWjY{{LqO zB@jsZ;^qN~CwyjT_y7@>0jDpBumeIElI*y7N;dDRZQ!b}RR=lG;14{x-C$JY5MY3& zLWJ-w