【LLVM】nsw和nuw的一个例子

2024-02-20 12:52
文章标签 例子 llvm nsw nuw

本文主要是介绍【LLVM】nsw和nuw的一个例子,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

nsw和nuw是LLVMIR提供给二元运算的flag。分别表示not signed wrap和not unsigned wrap。在LLVM2.6的更新日志中表述如下:
The add, sub and mul instructions now support optional “nsw” and “nuw” bits which indicate that the operation is guaranteed to not overflow (in the signed or unsigned case, respectively). This gives the optimizer more information and can be used for things like C signed integer values, which are undefined on overflow.
给出了和官方文档中不同的角度,该flag的添加相当于向编译器提供了一个不溢出的保证,让编译器基于此进行优化。

一个例子

在查找相关资料时,发现了一篇帖子,是LLVM开发者讨论nsw和非nsw的相似性,当两者共同出现时,GVN的消除方式是否安全。

int func(int a, int b) {int c = a + b;return c;
}

上述例子使用clang14进行编译:

clang -emit-llvm -S -O example.cc

得到的IR为:

; ModuleID = 'nuw_and_nsw/nsw_example1.cc'
source_filename = "nuw_and_nsw/nsw_example1.cc"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn
define dso_local noundef i32 @_Z4funcii(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {%3 = add nsw i32 %1, %0ret i32 %3
}attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"uwtable", i32 1}
!2 = !{!"clang version 14.0.0"}

其中对add运算添加了一个nsw的flag,因为在此例子中源代码蕴含的意思不考虑溢出。

修改之后给出一个IR,和帖子中给出的例子一致。

; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn
define dso_local noundef i32 @_Z4funcii(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {%3 = add nsw i32 %1, %0%4 = add i32 %1, %0ret i32 %4
}

通过如下命令调用GVN优化:

opt -gvn source.ll -o target.ll

优化结果的核心部分如下:

; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn
define dso_local noundef i32 @_Z4funcii(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {%3 = add i32 %1, %0ret i32 %3
}

另一种尝试输入的源IR如下:

; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn
define dso_local noundef i32 @_Z4funcii(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {%3 = add i32 %1, %0%4 = add nsw i32 %1, %0ret i32 %4
}

最后得到的优化结果依旧是将带有nsw的结果优化掉。

如何生成一个不带nsw的加法?

int func(unsigned a, unsigned b) {unsigned c = a + b;return (int)c;
}

生成的IR核心部分为:

; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn
define dso_local noundef i32 @_Z4funcjj(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 {%3 = add i32 %1, %0ret i32 %3
}

生成nsw flag的主要代码

首先关注clang/lib/CodeGen/CGExprScalar.cpp中的一个名为EmitAdd函数,该函数可以创建含有nsw flag的IR。现将部分函数片段摘录如下:

Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) {if (op.LHS->getType()->isPointerTy() ||op.RHS->getType()->isPointerTy())return emitPointerArithmetic(CGF, op, CodeGenFunction::NotSubtraction);if (op.Ty->isSignedIntegerOrEnumerationType()) {switch (CGF.getLangOpts().getSignedOverflowBehavior()) {case LangOptions::SOB_Defined:return Builder.CreateAdd(op.LHS, op.RHS, "add");case LangOptions::SOB_Undefined:if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))return Builder.CreateNSWAdd(op.LHS, op.RHS, "add");[[fallthrough]];case LangOptions::SOB_Trapping:if (CanElideOverflowCheck(CGF.getContext(), op))return Builder.CreateNSWAdd(op.LHS, op.RHS, "add");return EmitOverflowCheckedBinOp(op);}}

可以看到,首先判断两个操作数是否为指针,如果都不是,判断当前表达式是否是符号整数类型或枚举类型,然后根据CGF.getLangOpts().getSignedOverflowBehavior()判断不同的情况。
在clang/include/clang/Basic/LangOptions.h中可以找到SignedOverflowBehaviorTy的定义:

  enum SignedOverflowBehaviorTy {// Default C standard behavior.SOB_Undefined,// -fwrapvSOB_Defined,// -ftrapvSOB_Trapping};

可以看到,默认情况下SignedOverflowBehaviorTy的值为SOB_Undefined,而其余两行的flag分别是与溢出和陷阱相关的flag。可以参考笔者往期的文章。
如果将上述能够产生带有nsw的IR对应的编译过程添加-fwrapv flag,可以发现生成的IR中不再包含nsw flag。

一个包含nuw flag的例子

#include <stdio.h>
void func(unsigned a, unsigned b) {unsigned c = 0;if(a < 10000) {c = a + 10;}printf("%ud", c);
}

在该例子中,可以看到,a+10一定不会出现溢出的情况,因此智能的分析应该能够识别到此情况并将其设置为nuw和nsw。
上述程序生成的核心IR为:

; Function Attrs: mustprogress nofree nounwind uwtable
define dso_local void @_Z4funcjj(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 {
entry:%cmp = icmp ult i32 %a, 10000%add = add nuw nsw i32 %a, 10%spec.select = select i1 %cmp, i32 %add, i32 0%call = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str, i32 noundef %spec.select)ret void
}

也就是说,包含nuw和nsw的表达式是比未包含上述情况的表达式更precise的,因此如果在优化中遇到了这两种同时出现的情况,应该注意不能直接将未包含nsw和nuw的那个表达式删除。这样会造成分析的精度下降。

通过调试发现,-O0生成的add表达式是不包含nuw和nsw flag的,因此是通过后续的优化将其设置上的。简化的调用栈如下:

llvm::Instruction::setHasNoUnsignedWrap llvm/lib/IR/Instruction.cpp:349
llvm::refineInstruction llvm/lib/Transforms/Utils/SCCPSolver.cpp:130

再次印证了nuw和nsw是和优化密切相关的两个flag。

这篇关于【LLVM】nsw和nuw的一个例子的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/728270

相关文章

JavaFX环境的搭建和一个简单的例子

之前在网上搜了很多与javaFX相关的资料,都说要在Eclepse上要安装sdk插件什么的,反正就是乱七八糟的一大片,最后还是没搞成功,所以我在这里写下我搭建javaFX成功的环境给大家做一个参考吧。希望能帮助到你们! 1.首先要保证你的jdk版本能够支持JavaFX的开发,jdk-7u25版本以上的都能支持,最好安装jdk8吧,因为jdk8对支持JavaFX有新的特性了,比如:3D等;

javaScript日期相加减例子

当前时间加上2天 var d = new Date(“2015-7-31”); d.setDate(d.getDate()+2); var addTwo=d.getFullYear()+”年”+(d.getMonth()+1)+”月”+d.getDate()+”日”; “控制台输出===============”+”当前日期加2天:”+addTwo; 使用这种方法,月份也会给你计算.

LLVM入门2:如何基于自己的代码生成IR-LLVM IR code generation实例介绍

概述 本节将通过一个简单的例子来介绍如何生成llvm IR,以Kaleidoscope IR中的例子为例,我们基于LLVM接口构建一个简单的编译器,实现简单的语句解析并转化为LLVM IR,生成对应的LLVM IR部分,代码如下,文件名为toy.cpp,先给出代码,后面会详细介绍每一步分代码: #include "llvm/ADT/APFloat.h"#include "llvm/ADT/S

设计模式大全和详解,含Python代码例子

若有不理解,可以问一下这几个免费的AI网站 https://ai-to.cn/chathttp://m6z.cn/6arKdNhttp://m6z.cn/6b1quhhttp://m6z.cn/6wVAQGhttp://m6z.cn/63vlPw 下面是设计模式的简要介绍和 Python 代码示例,涵盖主要的创建型、结构型和行为型模式。 一、创建型模式 1. 单例模式 (Singleton

JSP 简单表单显示例子

<html><!--http://localhost:8080/test_jsp/input.html --><head><meta http-equiv="Content-Type" content="text/HTML; charset=utf-8"><title>input页面</title></head><body><form action="input.jsp" method

shell循环sleep while例子 条件判断

i=1# 小于5等于时候才执行while [ ${i} -le 5 ]doecho ${i}i=`expr ${i} + 1`# 休眠3秒sleep 3doneecho done 参考 http://c.biancheng.net/cpp/view/2736.html

【ReactJS】通过一个例子学习React组件的生命周期

源代码 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Reac

LLVM IR指令VM混淆分析

未混淆编译  编写一个最简单的测试代码,对 test_add函数用于对两个数相加: int __attribute((__annotate__("vm"))) test_add(int a, int b) {int c = a + b;return c;}int main(void) {int c = test_add(1, 2);return c;} 编译成中间代码:  未加

简单的android Listview使用例子

为了熟悉Listview的使用,做了一个小例子联系一下, 主要步骤: 1. 在MainActivity中,创建一个adapter对象(可以是android自带的ArrayAdapter,也可以是自定义的如SongAdapter) 2. 如果自定义,就要创建ListView的子项,如song_listview_item.xml 3. 创建ListView对象,并用setAdapter方法把a

【 python pymongo】使用pymongo的例子

MongoDB优点 MongoDB是一个为当代web应用而生的noSQL数据库,它有如下优点: 1、文档型存储。可以把关系型数据库的表理解为一个电子表格,列表示字段,每行的记录其实是按照列的字段顺序排列的值得元组。而存储在MongoDB中的文档被存储为键-值对的形式,值却可以是任意类型且可以嵌套。之前在用关系型数据库的时候,我们把产品信息打散到不同的表中,要通过关系表或者使用join拼接成复杂