Page
1
Modern X86 Assembly Language Programming Covers X86 64-bit, AVX, AVX2, and AVX-512 — Third Edition — Daniel Kusswurm
Page
2
Daniel Kusswurm Geneva, IL, USA Modern X86 Assembly Language Programming: Covers X86 64-bit, AVX, AVX2, and AVX-512 ISBN-13 (pbk): 978-1-4842-9602-8 ISBN-13 (electronic): 978-1-4842-9603-5 https://doi.org/10.1007/978-1-4842-9603-5 Copyright © 2023 by Daniel Kusswurm This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed. Trademarked names, logos, and images may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, logo, or image we use the names, logos, and images only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to proprietary rights. While the advice and information in this book are believed to be true and accurate at the date of publication, neither the authors nor the editors nor the publisher can accept any legal responsibility for any errors or omissions that may be made. The publisher makes no warranty, express or implied, with respect to the material contained herein. Managing Director, Apress Media LLC: Welmoed Spahr Acquisitions Editor: Celestin Suresh John Development Editor: James Markham Coordinating Editor: Mark Powers Cover designed by eStudioCalamar Cover image by Bolivia Inteligente on Unsplash (www.unsplash.com) Distributed to the book trade worldwide by Apress Media, LLC, 1 New York Plaza, New York, NY 10004, U.S.A. Phone 1-800-SPRINGER, fax (201) 348-4505, e-mail orders-ny@springer-sbm.com, or visit www.springeronline.com. Apress Media, LLC is a California LLC and the sole member (owner) is Springer Science + Business Media Finance Inc (SSBM Finance Inc). SSBM Finance Inc is a Delaware corporation. For information on translations, please e-mail booktranslations@springernature.com; for reprint, paperback, or audio rights, please e-mail bookpermissions@springernature.com. Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook versions and licenses are also available for most titles. For more information, reference our Print and eBook Bulk Sales web page at http://www.apress.com/bulk-sales. Any source code or other supplementary material referenced by the author in this book is available to readers on GitHub (https://github.com/Apress). For more detailed information, please visit https://www.apress.com/gp/services/source-code. Paper in this product is recyclable
Page
3
iii About the Author ����������������������������������������������������������������������������������������������������� xi About the Technical Reviewer ������������������������������������������������������������������������������� xiii Acknowledgments ���������������������������������������������������������������������������������������������������xv Introduction �����������������������������������������������������������������������������������������������������������xvii ■Chapter 1: X86-64 Core Architecture �������������������������������������������������������������������� 1 Historical Overview ���������������������������������������������������������������������������������������������������������� 1 Data Types ������������������������������������������������������������������������������������������������������������������������ 3 Fundamental Data Types ������������������������������������������������������������������������������������������������������������������������ 3 Numerical Data Types ����������������������������������������������������������������������������������������������������������������������������� 4 SIMD Data Types ������������������������������������������������������������������������������������������������������������������������������������� 5 Miscellaneous Data Types ���������������������������������������������������������������������������������������������������������������������� 7 X86-64 Processor Architecture ���������������������������������������������������������������������������������������� 8 General-Purpose Registers ��������������������������������������������������������������������������������������������������������������������� 9 Instruction Pointer �������������������������������������������������������������������������������������������������������������������������������� 10 RFLAGS Register ����������������������������������������������������������������������������������������������������������������������������������� 10 Floating-Point and SIMD Registers ������������������������������������������������������������������������������������������������������� 12 MXCSR Register ������������������������������������������������������������������������������������������������������������������������������������ 14 Instruction Operands ������������������������������������������������������������������������������������������������������ 15 Memory Addressing ������������������������������������������������������������������������������������������������������� 16 Condition Codes ������������������������������������������������������������������������������������������������������������� 18 Differences Between X86-64 and X86-32 ���������������������������������������������������������������������� 20 Legacy Instruction Sets ������������������������������������������������������������������������������������������������������������������������ 23 Summary ������������������������������������������������������������������������������������������������������������������������ 23 Table of Contents
Page
4
iv ■Chapter 2: X86-64 Core Programming – Part 1 ��������������������������������������������������� 25 Source Code Overview ��������������������������������������������������������������������������������������������������� 25 Assembler Basics ����������������������������������������������������������������������������������������������������������� 27 Integer Arithmetic ����������������������������������������������������������������������������������������������������������� 27 Integer Addition and Subtraction – 32-Bit �������������������������������������������������������������������������������������������� 27 Bitwise Logical Operations ������������������������������������������������������������������������������������������������������������������� 32 Shift Operations ������������������������������������������������������������������������������������������������������������������������������������ 36 Integer Addition and Subtraction – 64-bit �������������������������������������������������������������������������������������������� 41 Integer Multiplication and Division ������������������������������������������������������������������������������������������������������� 45 Summary ������������������������������������������������������������������������������������������������������������������������ 50 ■Chapter 3: X86-64 Core Programming – Part 2 ��������������������������������������������������� 51 Simple Stack Arguments ������������������������������������������������������������������������������������������������ 51 Mixed-Type Integer Arithmetic ��������������������������������������������������������������������������������������� 59 Memory Addressing Modes �������������������������������������������������������������������������������������������� 68 Condition Codes ������������������������������������������������������������������������������������������������������������� 75 Assembly Language For-Loops �������������������������������������������������������������������������������������� 81 Summary ������������������������������������������������������������������������������������������������������������������������ 87 ■Chapter 4: X86-64 Core Programming – Part 3 ��������������������������������������������������� 89 Arrays ����������������������������������������������������������������������������������������������������������������������������� 89 One-Dimensional Arrays ����������������������������������������������������������������������������������������������������������������������� 89 Multiple One-Dimensional Arrays ��������������������������������������������������������������������������������������������������������� 94 Two-Dimensional Arrays ��������������������������������������������������������������������������������������������������������������������� 101 Strings �������������������������������������������������������������������������������������������������������������������������� 107 Counting Characters ��������������������������������������������������������������������������������������������������������������������������� 107 Array Compare ������������������������������������������������������������������������������������������������������������������������������������ 112 Array Copy and Fill ������������������������������������������������������������������������������������������������������������������������������ 116 Array Reversal ������������������������������������������������������������������������������������������������������������������������������������ 120 Assembly Language Structures������������������������������������������������������������������������������������ 125 Summary ���������������������������������������������������������������������������������������������������������������������� 130 ■ Table of ConTenTs
Page
5
v ■Chapter 5: AVX Programming – Scalar Floating-Point �������������������������������������� 133 Floating-Point Programming Concepts ������������������������������������������������������������������������ 133 Scalar Floating-Point Registers ������������������������������������������������������������������������������������ 137 Single-Precision Floating-Point Arithmetic ������������������������������������������������������������������ 137 Temperature Conversions ������������������������������������������������������������������������������������������������������������������� 137 Cone Volume/Surface Area Calculation ���������������������������������������������������������������������������������������������� 142 Double-Precision Floating-Point Arithmetic ����������������������������������������������������������������� 148 Floating-Point Comparisons and Conversions �������������������������������������������������������������� 153 Floating-Point Comparisons ��������������������������������������������������������������������������������������������������������������� 153 Floating-Point Conversions ����������������������������������������������������������������������������������������������������������������� 163 Floating-Point Arrays ���������������������������������������������������������������������������������������������������� 175 Summary ���������������������������������������������������������������������������������������������������������������������� 182 ■Chapter 6: Run-Time Calling Conventions ��������������������������������������������������������� 185 Calling Convention Overview ���������������������������������������������������������������������������������������� 185 Calling Convention Requirements for Visual C++ �������������������������������������������������������� 186 Stack Frames �������������������������������������������������������������������������������������������������������������������������������������� 186 Using Non-volatile General-Purpose Registers����������������������������������������������������������������������������������� 191 Using Non-volatile XMM Registers ����������������������������������������������������������������������������������������������������� 196 Calling External Functions ������������������������������������������������������������������������������������������������������������������ 203 Calling Convention Requirements for GNU C++ ����������������������������������������������������������� 211 Stack Arguments �������������������������������������������������������������������������������������������������������������������������������� 211 Using Non-volatile General-Purpose Registers and Stack Frames ����������������������������������������������������� 215 Calling External Functions ������������������������������������������������������������������������������������������������������������������ 219 Summary ���������������������������������������������������������������������������������������������������������������������� 224 ■Chapter 7: Introduction to X86-AVX SIMD Programming ���������������������������������� 229 SIMD Programming Concepts �������������������������������������������������������������������������������������� 229 What Is SIMD? ������������������������������������������������������������������������������������������������������������������������������������ 229 SIMD Integer Arithmetic���������������������������������������������������������������������������������������������������������������������� 231 SIMD Floating-Point Arithmetic ���������������������������������������������������������������������������������������������������������� 235 SIMD Data Manipulation Operations ��������������������������������������������������������������������������������������������������� 237 ■ Table of ConTenTs
Page
6
vi X86-AVX Overview �������������������������������������������������������������������������������������������������������� 240 AVX/AVX2 SIMD Architecture Overview ������������������������������������������������������������������������ 241 SIMD Registers ����������������������������������������������������������������������������������������������������������������������������������� 242 SIMD Data Types ��������������������������������������������������������������������������������������������������������������������������������� 243 Instruction Syntax ������������������������������������������������������������������������������������������������������������������������������� 244 Differences Between X86-SSE and X86-AVX ��������������������������������������������������������������� 244 Summary ���������������������������������������������������������������������������������������������������������������������� 246 ■Chapter 8: AVX Programming – Packed Integers ���������������������������������������������� 247 Integer Arithmetic ��������������������������������������������������������������������������������������������������������� 247 Addition and Subtraction �������������������������������������������������������������������������������������������������������������������� 247 Multiplication �������������������������������������������������������������������������������������������������������������������������������������� 253 Bitwise Logical Operations ����������������������������������������������������������������������������������������������������������������� 260 Arithmetic and Logical Shifts ������������������������������������������������������������������������������������������������������������� 264 Integer Arrays ��������������������������������������������������������������������������������������������������������������� 268 Pixel Minimum and Maximum ������������������������������������������������������������������������������������������������������������ 268 Pixel Mean ������������������������������������������������������������������������������������������������������������������������������������������ 277 Summary ���������������������������������������������������������������������������������������������������������������������� 287 ■Chapter 9: AVX Programming – Packed Floating-Point ������������������������������������� 289 Packed Floating-Point Arithmetic ��������������������������������������������������������������������������������� 289 Elementary Operations ����������������������������������������������������������������������������������������������������������������������� 289 Packed Comparisons �������������������������������������������������������������������������������������������������������������������������� 298 Packed Conversions ��������������������������������������������������������������������������������������������������������������������������� 305 Packed Floating-Point Arithmetic – Arrays ������������������������������������������������������������������� 312 Mean and Standard Deviation ������������������������������������������������������������������������������������������������������������ 312 Distance Calculations ������������������������������������������������������������������������������������������������������������������������� 319 Packed Floating-Point Arithmetic – Matrices ��������������������������������������������������������������� 329 Column Means ������������������������������������������������������������������������������������������������������������������������������������ 329 Summary ���������������������������������������������������������������������������������������������������������������������� 338 ■ Table of ConTenTs
Page
7
vii ■Chapter 10: AVX2 Programming – Packed Integers ������������������������������������������ 341 Integer Arithmetic ��������������������������������������������������������������������������������������������������������� 341 Elementary Operations ����������������������������������������������������������������������������������������������������������������������� 342 Size Promotions ���������������������������������������������������������������������������������������������������������������������������������� 347 Image Processing ��������������������������������������������������������������������������������������������������������� 353 Pixel Clipping �������������������������������������������������������������������������������������������������������������������������������������� 354 RGB to Grayscale �������������������������������������������������������������������������������������������������������������������������������� 364 Pixel Conversions ������������������������������������������������������������������������������������������������������������������������������� 374 Image Histogram �������������������������������������������������������������������������������������������������������������������������������� 379 Summary ���������������������������������������������������������������������������������������������������������������������� 387 ■Chapter 11: AVX2 Programming – Packed Floating-Point – Part 1 ������������������� 389 Floating-Point Arrays ���������������������������������������������������������������������������������������������������� 389 Least Squares ������������������������������������������������������������������������������������������������������������������������������������� 390 Floating-Point Matrices ������������������������������������������������������������������������������������������������ 398 Matrix Multiplication ��������������������������������������������������������������������������������������������������������������������������� 398 Matrix (4 × 4) Multiplication ��������������������������������������������������������������������������������������������������������������� 410 Matrix (4 × 4) Vector Multiplication ���������������������������������������������������������������������������������������������������� 419 Covariance Matrix ������������������������������������������������������������������������������������������������������������������������������� 433 Summary ���������������������������������������������������������������������������������������������������������������������� 443 ■Chapter 12: AVX2 Programming – Packed Floating-Point – Part 2 ������������������� 445 Matrix Inversion ������������������������������������������������������������������������������������������������������������ 445 Single-Precision ��������������������������������������������������������������������������������������������������������������������������������� 446 Double-Precision �������������������������������������������������������������������������������������������������������������������������������� 462 Signal Processing – Convolutions �������������������������������������������������������������������������������� 474 1 D Convolution Arithmetic ������������������������������������������������������������������������������������������������������������������ 475 1 D Convolution Using Variable-Size Kernel ������������������������������������������������������������������ 477 Single-Precision ��������������������������������������������������������������������������������������������������������������������������������� 477 Double-Precision �������������������������������������������������������������������������������������������������������������������������������� 483 ■ Table of ConTenTs
Page
8
viii 1 D Convolution Using Fixed-Size Kernel ���������������������������������������������������������������������� 486 Single-Precision ��������������������������������������������������������������������������������������������������������������������������������� 486 Double-Precision �������������������������������������������������������������������������������������������������������������������������������� 490 Summary ���������������������������������������������������������������������������������������������������������������������� 493 ■Chapter 13: AVX-512 Programming – Packed Integers ������������������������������������� 495 AVX-512 Overview �������������������������������������������������������������������������������������������������������� 495 Execution Environment ����������������������������������������������������������������������������������������������������������������������� 496 Merge Masking and Zero Masking ����������������������������������������������������������������������������������������������������� 498 Embedded Broadcasts ������������������������������������������������������������������������������������������������������������������������ 500 Instruction-Level Rounding����������������������������������������������������������������������������������������������������������������� 501 Integer Arithmetic ��������������������������������������������������������������������������������������������������������� 502 Elementary Operations ����������������������������������������������������������������������������������������������������������������������� 502 Masked Operations ����������������������������������������������������������������������������������������������������������������������������� 510 Image Processing ��������������������������������������������������������������������������������������������������������� 521 Image Thresholding ���������������������������������������������������������������������������������������������������������������������������� 521 Image Statistics ���������������������������������������������������������������������������������������������������������������������������������� 530 Image Histogram �������������������������������������������������������������������������������������������������������������������������������� 543 Summary ���������������������������������������������������������������������������������������������������������������������� 547 ■Chapter 14: AVX-512 Programming – Packed Floating-Point – Part 1 �������������� 549 Floating-Point Arithmetic���������������������������������������������������������������������������������������������� 549 Elementary Operations ����������������������������������������������������������������������������������������������������������������������� 549 Packed Comparisons �������������������������������������������������������������������������������������������������������������������������� 559 Floating-Point Arrays ���������������������������������������������������������������������������������������������������� 566 Floating-Point Matrices ������������������������������������������������������������������������������������������������ 572 Covariance Matrix ������������������������������������������������������������������������������������������������������������������������������� 573 Matrix Multiplication ��������������������������������������������������������������������������������������������������������������������������� 577 Matrix (4 × 4) Vector Multiplication ���������������������������������������������������������������������������������������������������� 584 Summary ���������������������������������������������������������������������������������������������������������������������� 594 ■ Table of ConTenTs
Page
9
ix ■Chapter 15: AVX-512 Programming – Packed Floating-Point – Part 2 �������������� 595 Signal Processing ��������������������������������������������������������������������������������������������������������� 595 1 D Convolution Using Variable-Size Kernel ���������������������������������������������������������������������������������������� 595 1 D Convolution Using Fixed-Size Kernel �������������������������������������������������������������������������������������������� 604 Summary ���������������������������������������������������������������������������������������������������������������������� 610 ■Chapter 16: Advanced Assembly Language Programming ������������������������������� 611 CPUID Instruction ��������������������������������������������������������������������������������������������������������� 611 Processor Vendor Information ������������������������������������������������������������������������������������������������������������ 611 X86-AVX Detection ������������������������������������������������������������������������������������������������������������������������������ 615 Non-temporal Memory Stores �������������������������������������������������������������������������������������� 623 Integer Non-temporal Memory Stores ������������������������������������������������������������������������������������������������ 624 Floating-Point Non-temporal Memory Stores ������������������������������������������������������������������������������������� 627 SIMD Text Processing ��������������������������������������������������������������������������������������������������� 633 Summary ���������������������������������������������������������������������������������������������������������������������� 638 ■Chapter 17: Assembly Language Optimization and Development Guidelines ��� 639 Assembly Language Optimization Guidelines �������������������������������������������������������������� 639 Basic Techniques �������������������������������������������������������������������������������������������������������������������������������� 640 Floating-Point Arithmetic �������������������������������������������������������������������������������������������������������������������� 641 Branch Instructions ���������������������������������������������������������������������������������������������������������������������������� 642 Data Alignment ����������������������������������������������������������������������������������������������������������������������������������� 644 SIMD Techniques �������������������������������������������������������������������������������������������������������������������������������� 645 Assembly Language Development Guidelines�������������������������������������������������������������� 646 Identify Functions for x86-64 Assembly Language Coding ���������������������������������������������������������������� 646 Select Target x86-AVX Instruction Set ������������������������������������������������������������������������������������������������ 646 Establish Benchmark Timing Objectives ��������������������������������������������������������������������������������������������� 647 Code x86-64 Assembly Language Functions �������������������������������������������������������������������������������������� 647 Benchmark x86-64 Assembly Language Code ����������������������������������������������������������������������������������� 647 Optimize x86-64 Assembly Language Code ��������������������������������������������������������������������������������������� 647 ■ Table of ConTenTs
Page
10
x Repeat Benchmarking and Optimization Steps ���������������������������������������������������������������������������������� 647 Summary ���������������������������������������������������������������������������������������������������������������������� 648 Appendix A: Source Code and Development Tools ������������������������������������������������ 649 Appendix B: References and Resources ��������������������������������������������������������������� 667 Index ��������������������������������������������������������������������������������������������������������������������� 673 ■ Table of ConTenTs
Page
11
xi About the Author Daniel Kusswurm has 40+ years of professional experience as a software developer, computer scientist, and author. During his career, he has developed innovative software for medical devices, scientific instruments, and image processing applications. On many of these projects, he successfully employed x86 assembly language and the AVX, AVX2, and AVX-512 instruction sets to significantly improve the performance of computationally intense algorithms and solve unique programming challenges. His educational background includes a BS in electrical engineering technology from Northern Illinois University along with an MS and PhD in computer science from DePaul University. Daniel is also the author of multiple computer programming books including Modern Arm Assembly Language Programming (ISBN: 9781484262665) and Modern Parallel Programming with C++ and Assembly Language (ISBN: 9781484279175), both published by Apress.
Page
12
xiii About the Technical Reviewer Paul Cohen retired from Intel after 26 years. After being hired as Intel’s first Regional Software Specialist in New York, Paul spent his first 10 years at Intel in the sales force and won awards every year for being among the top performers. In 1990, Paul moved to Oregon to manage Intel’s early software marketing efforts for operating systems including both real-time and Intel’s UNIX. Following Intel's sale of this division, Paul joined the Intel Labs and was involved in the promotion of Native Signal Processing, which eventually became known as MMX/SSE. Before retiring, Paul managed Intel’s performance tools marketing group. Paul was an early member of the Intel startup team AnswerExpress, Intel’s first attempt at consumer software. This product was sold on QVC, a first for Intel. Paul led the advanced research team, prototyping concepts of hosting an application on the Web (SaaS – something unheard of in 2000) that ended up in the first version of the product. When the team was sold, Paul led another Intel startup team involved in the definition of AdvancedTCA Blade Servers primarily used by telecom companies and patented many concepts in use today by the current Blade Servers manufacturer. Paul has 18 patents and has a bachelor’s degree in Computer Science and an MSEE from Stony Brook University. Since retiring, Paul has traveled to over 75 countries and been on over 40 cruises.
Page
13
xv Acknowledgments It is impossible to write and publish a book without the dedication, expertise, and creativity of a professional behind-the-scenes team. This book is no exception. I would like to thank the talented editorial team at Apress including Celestin Suresh John, Nirmal Selvaraj, James Markham, and Mark Powers. I would also like to thank the entire production staff at Apress for their enthusiasm and hard work. Paul Cohen merits a sincere thank-you for his thorough technical review and constructive comments. Ed Kusswurm is appreciatively acknowledged for his methodical scrutiny of each chapter and sensible suggestions. I accept full responsibility for any remaining imperfections. Thanks to my professional colleagues for their support and encouragement. Finally, I would like to recognize parental nodes Armin (RIP) and Mary along with sibling nodes Mary, Tom, Ed, and John for their inspiration during the writing of this book.
Page
14
xvii Introduction Since the publication of Modern X86 Assembly Language Programming, First Edition, I have been asked on numerous occasions, “Do programmers still use assembly language?” The answer to this question is a resounding yes, but with a few caveats. Over the past several decades, software developers have used x86 assembly language to create innovative solutions for a wide variety of programming challenges. I can recall an era when it was common practice to code significant portions of a program (or even a complete application) using x86 assembly language. Nowadays, modern compilers can routinely generate machine code that is extremely efficient, both spatially and temporally, which means that the epoch of large assembly language programming projects is ancient history. However, use cases still exist where explicit assembly language coding is both feasible and computationally advantageous. One scenario where assembly language programming is potentially beneficial is in an application that wants to fully exploit the single-instruction multiple-data (SIMD) capabilities of a modern x86 processor. A SIMD-capable x86 processor incorporates hardware components and instructions that perform simultaneous calculations using multiple data values, which can significantly improve the performance of a computationally intense function or algorithm. Unfortunately, many compilers are unable to wholly utilize the SIMD capabilities of an x86 processor. Assembly language programming, on the other hand, empowers the software developer to fully exploit these computational resources. Modern X86 Assembly Language Programming Modern X86 Assembly Language Programming, Third Edition is an instructional text that teaches you how to code x86-64 assembly language functions. It also explains how you can algorithmically exploit the SIMD capabilities of an x86-64 processor. The content of this book is organized to help you quickly understand x86-64 assembly language code. In addition, the accompanying source code is structured to accelerate comprehension of x86-64 assembly language instruction syntax, programming constructs, and data structures. After reading this book, you will be able to code performance-accelerated functions and algorithms using x86-64 assembly language and the AVX, AVX2, and AVX-512 instruction sets. While it is still theoretically possible to write large sections or an entire application program using assembly language, the demanding requirements of contemporary software development mean that such an approach is both impractical and ill-advised. Instead, this book accentuates the coding x86-64 assembly language functions that are callable from C++. The downloadable software package for this book includes source code that works on both Windows (Visual C++ and MASM) and Linux (GNU C++ and NASM). Before proceeding, it warrants mentioning that this edition of the Modern X86 Assembly Language Programming book doesn’t cover x86-32 assembly language programming. It also doesn’t discuss legacy x86 technologies such as the x87 floating-point unit, MMX, and X86-SSE (Streaming SIMD Extensions). The first edition of this text remains relevant if you’re interested in learning about these topics. This book doesn’t explain x86 architectural features or privileged instructions that are used in operating systems and device drivers. However, if your goal is to develop x86-64 assembly language code for these use cases, you’ll need to thoroughly comprehend the material that’s presented in this book.
Page
15
xviii While perusing Modern X86 Assembly Language Programming, Third Edition, you should keep in mind that explicit assembly language coding is often a trade-off between performance gains, development effort, and future maintainability. Perhaps the most important reason for writing this book is to impart the x86 programming savoir faire that you’ll need to make informed decisions regarding these trade-offs. Target Audience The target audience for Modern X86 Assembly Language Programming, Third Edition includes: • Software developers who are creating programs for x86 platforms and want to learn how to code performance-enhanced algorithms using the core x86-64 instruction set • Software developers who need to learn how to write SIMD functions or accelerate the performance of new or existing code using the AVX, AVX2, and AVX-512 instruction sets • Computer science/engineering students or hobbyists who want to learn or better understand x86-64 assembly language programming and the AVX, AVX2, and AVX-512 instruction sets Readers of Modern X86 Assembly Language Programming, Third Edition should have previous programming experience with modern C++ (i.e., ISO C++11 or later). To run or modify the source code examples, some familiarity with Microsoft’s Visual Studio or the GNU toolchain will be helpful. Content Overview The primary objective of this book is to teach you x86 64-bit assembly language programming and how to exploit AVX, AVX2, and AVX-512 instruction sets. Here’s a brief overview of what you can expect to learn. X86 Core Architecture – Chapter 1 explains the core architecture of an x86-64 processor. Topics discussed include fundamental data types, registers, status flags, memory addressing modes, and other important architectural details. X86-64 Core Programming – Chapter 2 introduces the fundamentals of x86-64 assembly language programming. The source code examples illustrate essential x86-64 assembly language programming concepts including integer arithmetic, bitwise logical operations, and shift instructions. This chapter also explains basic assembler usage and assembly language syntax. Chapter 3 explores additional core x86-64 assembly language programming concepts including advanced integer arithmetic, memory addressing modes, and condition codes. This chapter also covers stack usage and for-loops. Chapter 4 explains x86-64 assembly language coding using arrays and structures. It also describes how to utilize x86-64 string processing instructions. AVX Programming – Scalar Floating-Point – Chapter 5 explains scalar floating-point arithmetic and other operations using x86-64 assembly language and the AVX instruction set. It also outlines the C++ calling convention requirements for scalar floating-point arguments and return values. Run-Time Calling Conventions – Chapter 6 formally describes the run-time calling conventions for x86-64 assembly language functions. The first section explains the requirements for Windows and Visual C++, while the second section covers Linux and GNU C++. Introduction to x86-AVX – Chapter 7 introduces the SIMD capabilities of Advanced Vector Extensions (AVX). It includes a detailed discussion of AVX architecture and related topics. This chapter also explicates fundamental SIMD arithmetic and other SIMD programming concepts. AVX Programming – Chapter 8 spotlights packed integer arithmetic and other operations using AVX. It also describes how to code packed integer calculating functions using arrays and the AVX instruction set. Chapter 9 illustrates packed floating-point arithmetic and other operations using AVX. This chapter also demonstrates how to use AVX instructions to perform calculations using floating-point arrays and matrices. ■ InTroduCTIon
Page
16
xix AVX2 Programming – Chapter 10 describes AVX2 packed integer programming. Additionally, it elucidates the coding of several image processing algorithms using the AVX2 instruction set. Chapter 11 teaches you how to enhance the performance of universal floating-point array and matrix calculations using x86-64 assembly language and the AVX2 instruction set. You’ll also learn how to accelerate these types of calculations using fused-multiply-add (FMA) instructions. Chapter 12 explicates the coding of advanced algorithms including matrix inversions and convolutions using the AVX2 and FMA instruction sets. AVX-512 Programming – Chapter 13 presents an overview of AVX-512. It also explains basic packed integer arithmetic using 512-bit wide operands. Chapter 14 explores AVX-512 packed floating-point arithmetic and other operations. Chapter 15 illustrates the use of AVX-512 instructions to accelerate the performance of common matrix and signal processing operations. Advanced Topics – Chapter 16 demonstrates the use of the cpuid instruction. It also explains how to use non-temporal store instructions and perform SIMD text processing. Chapter 17 discusses guidelines that you can use to optimize the performance of your x86-64 assembly language code. This chapter also reviews several issues related to assembly language software development workflow. Appendixes – Appendix A explains how to download, install, and execute the source code examples. This appendix additionally includes some brief usage notes about the software development tools that were used to create the source code examples. Appendix B contains a list of references that were consulted during the writing of this book. It also provides some supplemental resources that you can consult for additional x86-64 assembly language programming information. Source Code The source code published in this book is available on GitHub at https://github.com//apress/ modern-x86-assembly-language-programming-3e. ■ Caution The sole purpose of the source code is to elucidate programming topics that are directly related to the content of this book. Minimal attention is given to essential software engineering concerns such as robust error handling, security risks, numerical stability, rounding errors, or ill-conditioned functions. You are responsible for addressing these concerns should you decide to use any of the source code in your own programs. The Windows source code examples were developed using Visual Studio 2022, MASM, and Windows 11. The source code examples will also work with Windows 10. The Linux source code examples were developed using GNU C++, Make, and NASM on Debian 11. They were also tested with Ubuntu 22.10. To execute the source code, the processor in your computer and its host operating system must support one or more of the following x86 instruction set extensions: AVX, AVX2, or AVX-512. Appendix A contains additional information about the source code and software development tools. Terminology Modern X86 Assembly Language Programming, Third Edition uses the expressions x86-64 and x86-32 to signify the 64-bit and 32-bit execution environments (i.e., registers, instructions, operands, memory addresses, data types, etc.) of an x86 processor. As mentioned earlier, the latter is not covered in this book, except for a few comparison references to aid those migrating from the 32-bit platform. The term x86 is employed for features that apply to both execution environments. The appellation x86-AVX is utilized as an umbrella expression for features that pertain to AVX, AVX2, and AVX-512. The individual acronyms ■ InTroduCTIon
Page
17
xx are employed when discussing functional aspects or computational capabilities of a specific x86-AVX instruction set extension. Similarly, the terms x86-SSE or SSE, SSE2, SSE3, etc., are applied when describing similarities or differences between these legacy SIMD technologies and x86-AVX. Additional Resources An extensive set of x86-related programming documentation is available from both AMD and Intel. Appendix B lists several important resources that both aspiring and experienced x86 assembly language programmers will find useful. Of all the resources listed in Appendix B, the most valuable reference is Volume 2 of Intel 64 and IA-32 Architectures Software Developer’s Manual, Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4 (www.intel.com/content/www/us/en/processors/architectures- software-developer-manuals.html). This tome contains comprehensive programming information for every x86 processor instruction including detailed operational descriptions, lists of valid operands, affected status flags, and potential exceptions. You are strongly encouraged to consult this indispensable resource when developing your own x86 assembly language code to verify correct instruction usage and execution outcomes. ■ InTroduCTIon
Page
18
1 CHAPTER 1 X86-64 Core Architecture This chapter explores the core architecture of an x86-64 processor from the perspective of an application program. It begins with a brief historical overview of the x86 platform. The purpose of this overview is to provide a frame of reference for subsequent content. Next is a review of x86-64 fundamental, numeric, and SIMD (single instruction multiple data) data types. This is followed by an examination of x86-64 core architecture, which includes processor register sets, status flags, instruction operands, and memory addressing modes. The chapter concludes with a summary of important differences between x86-32 and x86-64 assembly language programming for the benefit of those migrating from the former. Unlike languages such as C and C++, assembly language programming requires the software developer to comprehend specific architectural features of the target processor before attempting to write any code. The topics covered in this chapter will fulfill this requirement and provide a foundation for understanding the source code that’s presented in subsequent chapters. Comprehension of this chapter’s material is also necessary before you progress to the expositions that discuss AVX, AVX2, and AVX-512. Historical Overview Before examining the technical details of an x86-64 processor’s core architecture, it can be constructive to understand how the architecture has evolved over the years. The short review that follows focuses on noteworthy processors and instruction set enhancements that have affected how software developers use x86-64 assembly language. Readers who are interested in a more comprehensive chronicle of x86-64 processor lineage can consult the resources listed in Appendix A. The x86-64 processor platform is an extension of the original x86-32 platform. The first silicon embodiment of the x86-32 platform was the Intel 80386 microprocessor, which was introduced in 1985. The 80386 extended the architecture of the 16-bit 80286 to include 32-bit wide registers, native 32-bit arithmetic, flat memory model options, a 4 GB logical address space, and paged virtual memory. The 80486 processor improved the performance of the 80386 with the inclusion of on-chip memory caches and optimized instructions. Unlike the 80386 with its separate 80387 floating-point unit (FPU), most versions of the 80486 CPU included an integrated x87 FPU. Expansion of the x86-32 platform continued with the introduction of the first Pentium brand processor in 1993. Known as the P5 microarchitecture, performance enhancements included a dual-instruction execution pipeline, 64-bit external data bus, and separate on-chip memory caches for both code and data. Later versions (1997) of the P5 microarchitecture incorporated a new computational resource called MMX technology, which supports SIMD operations (i.e., a single instruction that concurrently manipulates multiple data values) on packed integers using 64-bit wide registers. A packed integer is a collection of multiple data elements (e.g., eight 8-bit integers or four 16-bit integers) that enables the processor to perform various calculating operations (e.g., arithmetic, comparisons, conversions, etc.) on each independent element simultaneously. © Daniel Kusswurm 2023 D. Kusswurm, Modern X86 Assembly Language Programming, https://doi.org/10.1007/978-1-4842-9603-5_1
Page
19
2 The P6 microarchitecture, first used on the Pentium Pro (1995) and Pentium II (1997), extended the x86-32 platform using a three-way superscalar design. This means that the processor is able (on average) to decode, dispatch, and execute three distinct instructions during each clock cycle. Other P6 augmentations included out-of-order instruction executions, improved branch prediction algorithms, and speculative executions. The Pentium III, also based on the P6 microarchitecture, was launched in 1999 and included a new SIMD technology called Streaming SIMD Extensions (SSE). SSE adds eight 128-bit wide registers to the x86-32 platform and instructions that perform packed single-precision floating-point arithmetic. In 2000, Intel introduced a new microarchitecture called Netburst that included SSE2, which extended the floating-point capabilities of SSE to cover packed double-precision values. SSE2 also incorporated additional instructions that enabled the 128-bit SSE registers to be used for packed integer calculations and scalar floating-point operations. Processors based on the Netburst architecture included several variations of the Pentium 4. In 2004, the Netburst microarchitecture was upgraded to include SSE3 and hyper-threading technology. SSE3 adds new packed integer and packed floating-point instructions to the x86 platform, while hyper-threading technology parallelizes the processor’s front-end instruction pipelines to improve performance. SSE3-capable processors include 90 nm (and smaller) versions of the Pentium 4 and Xeon product lines. In 2006, Intel launched a new microarchitecture called Core. The Core microarchitecture included redesigns of many Netburst front-end pipelines and execution units to improve performance and reduce power consumption. It also incorporated several SIMD enhancements including SSSE3 and SSE4.1. These extensions added new packed integer and packed floating-point instructions to the platform but no new registers or data types. Processors based on the Core microarchitecture include CPUs from the Core 2 Duo and Core 2 Quad series and Xeon 3000/5000 series. A microarchitecture called Nehalem followed Core in late 2008. The Nehalem microarchitecture incorporates SSE4.2. This final x86-SSE enhancement adds several application-specific accelerator instructions to the x86-SSE instruction set. SSE4.2 also incorporates new instructions that facilitate text string processing using 128-bit wide operands. Processors based on the Nehalem microarchitecture include first-generation Core i3, i5, and i7 CPUs. It also includes CPUs from the Xeon 3000, 5000, and 7000 series. In 2011, Intel released its next-generation microarchitecture called Sandy Bridge. The Sandy Bridge microarchitecture introduced a new x86 SIMD technology called Advanced Vector Extensions (AVX). AVX adds packed floating-point operations (both single-precision and double-precision) using 256-bit wide registers and operands. AVX also supports a new three-operand instruction syntax, which improves code efficiency by reducing the number of register-to-register data transfers that a software function must perform. Processors based on the Sandy Bridge microarchitecture include second- and third-generation Core i3, i5, and i7 CPUs along with Xeon V2 series CPUs. In 2013, Intel unveiled its Haswell microarchitecture. Haswell includes AVX2, which extends AVX to support packed integer operations using 256-bit wide registers and operands. AVX2 also supports enhanced data transfer capabilities with its broadcast, gather, and permute instructions. Another feature of the Haswell microarchitecture is its inclusion of fused-multiply-add (FMA) instructions. FMA enables software functions to perform product-sum (or dot product) calculations using a single floating-point rounding operation, which can improve both performance and accuracy. The Haswell microarchitecture also encompasses new general-purpose register instructions. Processors based on the Haswell microarchitecture include fourth- generation Core i3, i5, and i7 CPUs. AVX2 is also included in later generations of Core family CPUs and in Xeon V3, V4, and V5 series CPUs. X86 platform extensions over the past few decades have not been limited to SIMD enhancements. In 2003, AMD introduced its Opteron processor, which extended the x86’s execution environment from 32 bits to 64 bits. Intel followed suit in 2004 by adding essentially the same 64-bit extensions to its processors starting with certain versions of the Pentium 4. The x86-64 execution environment includes a larger address space, additional general-purpose registers, built-in 64-bit integer arithmetic, and other architectural extensions that improve processor performance. Processors from AMD have also evolved over the past decades. In 2003, AMD introduced a series of processors based on its K8 microarchitecture. Original versions of the K8 included support for MMX, SSE, and SSE2, while later versions added SSE3. In 2007, the K10 microarchitecture was launched and included Chapter 1 ■ X86-64 Core arChiteCture
Page
20
3 a SIMD enhancement called SSE4a. SSE4a contains several mask shift and streaming store instructions that are not available on processors from Intel. Following the K10, AMD introduced a new microarchitecture called Bulldozer in 2011. The Bulldozer microarchitecture includes SSSE3, SSE4.1, SSE4.2, SSE4a, and AVX. It also includes FMA4, which is a four-operand version of fused-multiply-add. Like SSE4a, processors marketed by Intel do not support FMA4 instructions. A 2012 update to the Bulldozer microarchitecture called Piledriver includes support for both FMA4 and the three-operand version of FMA, which is often called FMA3 by some CPU feature detection utilities (Appendix B lists some of these utilities). The first AMD microarchitecture to support AVX2, named Excavator, was launched in 2015. Processors based on the subsequent Zen (2017), Zen+ (2018), Zen 2 (2019), Zen 3 (2020), and Zen 4 (2022) microarchitectures also include AVX2; however, they do not support FMA4. High-end desktop and server-oriented processors based on Intel’s Skylake-X microarchitecture, first marketed during 2017, include a new SIMD extension called AVX-512. This architectural enhancement supports packed integer and floating-point operations using 512-bit wide registers and operands. AVX-512 also incorporates architectural additions that facilitate instruction-level masking and merging, floating-point rounding control, and broadcast operations. AMD processors based on the Zen 4 microarchitecture also support AVX-512. Data Types X86-64 assembly language functions can use a wide variety of data types. Most of these types originate from a small set of fundamental data types that are intrinsic to the x86 platform. A programmer can employ these fundamental data types to perform assorted arithmetic and data manipulation operations using signed and unsigned integers, single- and double-precision floating-point values, text strings, or SIMD values. The remainder of this section describes the x86 data types that are used in x86-64 assembly language functions. Fundamental Data Types A fundamental data type is an elementary unit of data that the processor manipulates during program execution. The x86-64 platform supports fundamental data types ranging from 8 bits (1 byte) to 128 bits (16 bytes). Table 1-1 shows these types along with typical usages. Table 1-1. Fundamental Data Types Data Type Size (Bits) Typical Use Byte 8 Text characters, small integers Word 16 Text characters, integers Doubleword 32 Integers, single-precision floating-point Quadword 64 Integers, double-precision floating-point, memory address Double Quadword 128 Integers, packed integers, packed floating-point The fundamental data types shown in Table 1-1 are, unsurprisingly, integer powers of two. The bits of a fundamental data type are numbered from right to left with 0 and size – 1 used to identify the least and most significant bits, respectively. Fundamental data types larger than a single byte are stored in memory using little-endian byte ordering. In little-endian byte ordering, the processor stores the least significant byte of a multibyte value at the lowest memory address. Figure 1-1 illustrates the bit numbering and byte ordering schemes used by the fundamental data types shown in Table 1-1. Chapter 1 ■ X86-64 Core arChiteCture