| // Copyright 2019 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package s390x |
| |
| import ( |
| "math/bits" |
| ) |
| |
| // RotateParams represents the immediates required for a "rotate |
| // then ... selected bits instruction". |
| // |
| // The Start and End values are the indexes that represent |
| // the masked region. They are inclusive and are in big- |
| // endian order (bit 0 is the MSB, bit 63 is the LSB). They |
| // may wrap around. |
| // |
| // Some examples: |
| // |
| // Masked region | Start | End |
| // --------------------------+-------+---- |
| // 0x00_00_00_00_00_00_00_0f | 60 | 63 |
| // 0xf0_00_00_00_00_00_00_00 | 0 | 3 |
| // 0xf0_00_00_00_00_00_00_0f | 60 | 3 |
| // |
| // The Amount value represents the amount to rotate the |
| // input left by. Note that this rotation is performed |
| // before the masked region is used. |
| type RotateParams struct { |
| Start int8 // big-endian start bit index [0..63] |
| End int8 // big-endian end bit index [0..63] |
| Amount int8 // amount to rotate left |
| } |
| |
| // NewRotateParams creates a set of parameters representing a |
| // rotation left by the amount provided and a selection of the bits |
| // between the provided start and end indexes (inclusive). |
| // |
| // The start and end indexes and the rotation amount must all |
| // be in the range 0-63 inclusive or this function will panic. |
| func NewRotateParams(start, end, amount int8) RotateParams { |
| if start&^63 != 0 { |
| panic("start out of bounds") |
| } |
| if end&^63 != 0 { |
| panic("end out of bounds") |
| } |
| if amount&^63 != 0 { |
| panic("amount out of bounds") |
| } |
| return RotateParams{ |
| Start: start, |
| End: end, |
| Amount: amount, |
| } |
| } |
| |
| // RotateLeft generates a new set of parameters with the rotation amount |
| // increased by the given value. The selected bits are left unchanged. |
| func (r RotateParams) RotateLeft(amount int8) RotateParams { |
| r.Amount += amount |
| r.Amount &= 63 |
| return r |
| } |
| |
| // OutMask provides a mask representing the selected bits. |
| func (r RotateParams) OutMask() uint64 { |
| // Note: z must be unsigned for bootstrap compiler |
| z := uint8(63-r.End+r.Start) & 63 // number of zero bits in mask |
| return bits.RotateLeft64(^uint64(0)<<z, -int(r.Start)) |
| } |
| |
| // InMask provides a mask representing the selected bits relative |
| // to the source value (i.e. pre-rotation). |
| func (r RotateParams) InMask() uint64 { |
| return bits.RotateLeft64(r.OutMask(), -int(r.Amount)) |
| } |
| |
| // OutMerge tries to generate a new set of parameters representing |
| // the intersection between the selected bits and the provided mask. |
| // If the intersection is unrepresentable (0 or not contiguous) nil |
| // will be returned. |
| func (r RotateParams) OutMerge(mask uint64) *RotateParams { |
| mask &= r.OutMask() |
| if mask == 0 { |
| return nil |
| } |
| |
| // normalize the mask so that the set bits are left aligned |
| o := bits.LeadingZeros64(^mask) |
| mask = bits.RotateLeft64(mask, o) |
| z := bits.LeadingZeros64(mask) |
| mask = bits.RotateLeft64(mask, z) |
| |
| // check that the normalized mask is contiguous |
| l := bits.LeadingZeros64(^mask) |
| if l+bits.TrailingZeros64(mask) != 64 { |
| return nil |
| } |
| |
| // update start and end positions (rotation amount remains the same) |
| r.Start = int8(o+z) & 63 |
| r.End = (r.Start + int8(l) - 1) & 63 |
| return &r |
| } |
| |
| // InMerge tries to generate a new set of parameters representing |
| // the intersection between the selected bits and the provided mask |
| // as applied to the source value (i.e. pre-rotation). |
| // If the intersection is unrepresentable (0 or not contiguous) nil |
| // will be returned. |
| func (r RotateParams) InMerge(mask uint64) *RotateParams { |
| return r.OutMerge(bits.RotateLeft64(mask, int(r.Amount))) |
| } |