转: __asm__ __volatile__内嵌汇编用法简述

时间:2022-09-22 17:08:49

from: http://www.embedu.org/Column/Column28.htm

__asm__ __volatile__内嵌汇编用法简述

作者:刘老师,华清远见嵌入式学院高级讲师,ARM ATC授权培训讲师。

__asm__ __volatile__内嵌汇编用法简述 在阅读C/C++原码时经常会遇到内联汇编的情况,下面简要介绍下__asm__ __volatile__内嵌汇编用法。因为我们华清远见教学平台是ARM体系结构的,所以下面的示例都是用ARM汇编。

带有C/C++表达式的内联汇编格式为:

__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);

其中每项的概念及功能用法描述如下:

1、 __asm__

__asm__是GCC 关键字asm 的宏定义:

#define __asm__ asm

__asm__或asm 用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头的,是必不可少的。

2、Instruction List

Instruction List 是汇编指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或 __asm__ ("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。但并非所有Instruction List 为空的内联汇编表达式都是没有意义的,比如:__asm__ ("":::"memory");

就非常有意义,它向GCC 声明:“内存作了改动”,GCC 在编译的时候,会将此因素考虑进去。 当在"Instruction List"中有多条指令的时候,可以在一对引号中列出全部指令,也可以将一条 或几条指令放在一对引号中,所有指令放在多对引号中。如果是前者,可以将每一条指令放在一行,如果要将多条指令放在一行,则必须用分号(;)或换行符(\n)将它们分开. 综上述:(1)每条指令都必须被双引号括起来 (2)两条指令必须用换行或分号分开。

例如: 在ARM系统结构上关闭中断的操作

int disable_interrupts (void) 
        { 
                unsigned long old,temp; 
                __asm__ __volatile__("mrs %0, cpsr\n" 
                                "orr %1, %0, #0x80\n" 
                                "msr cpsr_c, %1" 
                                : "=r" (old), "=r" (temp) 
                                : 
                                : "memory"); 
                return (old & 0x80) == 0; 
        }

3. __volatile__

__volatile__是GCC 关键字volatile 的宏定义

#define __volatile__ volatile

__volatile__或volatile 是可选的。如果用了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。

4、 Output

Output 用来指定当前内联汇编语句的输出

例如:从arm协处理器p15中读出C1值

static unsigned long read_p15_c1 (void) 
        { 
                unsigned long value; 
                __asm__ __volatile__( 
                                "mrc p15, 0, %0, c1, c0, 0 @ read control reg\n" 
                                : "=r" (value) @编译器选择一个R*寄存器 
                                : 
                                : "memory"); 
        #ifdef MMU_DEBUG 
                printf ("p15/c1 is = %08lx\n", value); 
        #endif 
                return value; 
        }

5、 Input

Input 域的内容用来指定当前内联汇编语句的输入Output和Input中,格式为形如“constraint”(variable)的列表(逗号分隔)

例如:向arm协处理器p15中写入C1值

static void write_p15_c1 (unsigned long value) 
        { 
        #ifdef MMU_DEBUG 
                printf ("write %08lx to p15/c1\n", value); 
        #endif 
                __asm__ __volatile__( 
                                "mcr p15, 0, %0, c1, c0, 0 @ write it back\n" 
                                : 
                                : "r" (value) @编译器选择一个R*寄存器 
                                : "memory"); 
                read_p15_c1 (); 
        }

6.、Clobber/Modify

有时候,你想通知GCC当前内联汇编语句可能会对某些寄存器或内存进行修改,希望GCC在编译时能够将这一点考虑进去。那么你就可以在Clobber/Modify域声明这些寄存器或内存。这种情况一般发生在一个寄存器出现在"Instruction List",但却不是由Input/Output操作表达式所指定的,也不是在一些Input/Output操作表达式使用"r"约束时由GCC 为其选择的,同时此寄存器被"Instruction List"中的指令修改,而这个寄存器只是供当前内联汇编临时使用的情况。

例如:

__asm__ ("mov R0, #0x34" : : : "R0");

寄存器R0出现在"Instruction List中",并且被mov指令修改,但却未被任何Input/Output操作表达式指定,所以你需要在Clobber/Modify域指定"R0",以让GCC知道这一点。

因为你在Input/Output操作表达式所指定的寄存器,或当你为一些Input/Output操作表达式使用"r"约束,让GCC为你选择一个寄存器时,GCC对这些寄存器是非常清楚的——它知道这些寄存器是被修改的,你根本不需要在Clobber/Modify域再声明它们。但除此之外, GCC对剩下的寄存器中哪些会被当前的内联汇编修改一无所知。所以如果你真的在当前内联汇编指令中修改了它们,那么就最好在Clobber/Modify 中声明它们,让GCC针对这些寄存器做相应的处理。否则有可能会造成寄存器的不一致,从而造成程序执行错误。

如果一个内联汇编语句的Clobber/Modify域存在"memory",那么GCC会保证在此内联汇编之前,如果某个内存的内容被装入了寄存器,那么在这个内联汇编之后,如果需要使用这个内存处的内容,就会直接到这个内存处重新读取,而不是使用被存放在寄存器中的拷贝。因为这个 时候寄存器中的拷贝已经很可能和内存处的内容不一致了。

这只是使用"memory"时,GCC会保证做到的一点,但这并不是全部。因为使用"memory"是向GCC声明内存发生了变化,而内存发生变化带来的影响并不止这一点。

例如:

int main(int __argc, char* __argv[]) 
        { 
        int* __p = (int*)__argc; 
        (*__p) = 9999; 
        __asm__("":::"memory"); 
        if((*__p) == 9999) 
        return 5; 
        return (*__p); 
        }

本例中,如果没有那条内联汇编语句,那个if语句的判断条件就完全是一句废话。GCC在优化时会意识到这一点,而直接只生成return 5的汇编代码,而不会再生成if语句的相关代码,而不会生成return (*__p)的相关代码。但你加上了这条内联汇编语句,它除了声明内存变化之外,什么都没有做。但GCC此时就不能简单的认为它不需要判断都知道 (*__p)一定与9999相等,它只有老老实实生成这条if语句的汇编代码,一起相关的两个return语句相关代码。

另外在linux内核中内存屏障也是基于它实现的include/asm/system.h中

# define barrier() _asm__volatile_("": : :"memory")

主要是保证程序的执行遵循顺序一致性。呵呵,有的时候你写代码的顺序,不一定是最终执行的顺序,这个是处理器有关的。

转: __asm__ __volatile__内嵌汇编用法简述的更多相关文章

  1. Linux内核--C语言中内嵌汇编 asm __volatile__

    在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C 变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可 ...

  2. Linux内核系列—C语言中内嵌汇编 asm __volatile__,asm__volatile_【转】

    转自:http://www.bkjia.com/Androidjc/1109412.html 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器, ...

  3. GCC在C语言中内嵌汇编 asm __volatile__ 【转】

    转自:http://blog.csdn.net/pbymw8iwm/article/details/8227839 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达 ...

  4. GCC在C语言中内嵌汇编 asm __volatile__

    2012-11-26 22:20 17958人阅读 评论(2) 收藏 举报  分类: linux(59)  架构管理(24)  C/C++(59)  目录(?)[+] 在内嵌汇编中,可以将C语言表达式 ...

  5. Beennan的内嵌汇编指导(译)Brennan's Guide to Inline Assembly

    注:写在前面,这是一篇翻译文章,本人的英文水平很有限,但内嵌汇编是学习操作系统不可少的知识,本人也常去查看这方面的内容,本文是在做mit的jos实验中的一篇关于内嵌汇编的介绍.关于常用的内嵌汇编(AT ...

  6. 【原创】X86_64汇编、寄存器、内嵌汇编

    整理的X86_64/X86汇编.寄存器.C内嵌汇编笔记,主要用于查阅使用. 目录 一.汇编语言 二.指令 数据传输指令 栈操作指令 push pop 运算指令 位操作 比较操作指令 标志寄存器 流控制 ...

  7. ARM GCC 内嵌汇编手册

    转自:http://blogold.chinaunix.net/u2/69404/showart_1922655.html ARM GCC 内嵌(inline)汇编手册 关于这篇文档这篇文章是本人为方 ...

  8. arm汇编(c内嵌汇编及c和汇编互调)

    C语言编译成汇编: arm-linux-gcc -S test.c -o test.S C语言编译成可执行文件: arm-linux-gcc test.c -o test 多个文件编译链接: arm- ...

  9. C语言内嵌汇编(arm-v7)----加减乘移位

    在现代嵌入式操作系统中,汇编语言当然必不可少,汇编语言的优势就是执行速度快.如果在C语言的代码中,在关键的地方内嵌汇编,那么效率将会大大的提高,我们来看看代码: #include <stdio. ...

随机推荐

  1. 每天一个linux命令(8):cp 命令

    cp命令用来复制文件或者目录,是Linux系统中最常用的命令之一.一般情况下,shell会设置一个别名,在命令行下复制文件时,如果目标文件已经存在,就会询问是否覆盖,不管你是否使用-i参数.但是如果是 ...

  2. 学习ng2,从zonejs开始(非官方翻译) ----angular2系列(一&rpar;

    Zone是什么: 官方解释:zone.js为JavaScript提供了执行上下文,可以在异步任务之间进行持久性传递. 最开始我一直没理解到这句话,学习过程中我也因为自己的一些失误而一直纠结徘徊,情况是 ...

  3. Javascript小括号&OpenCurlyDoubleQuote;&lpar;&rpar;”的多义性

    摘要:本文主要介绍JavaScript中小括号有五种语义. Javascript中小括号有五种语义 语义1,函数声明时参数表 function func(arg1,arg2){ // ... } 语义 ...

  4. Java设计模式--Java Builder模式

    1.Java Builder模式主要是用一个内部类去实例化一个对象,避免一个类出现过多构造函数,而且构造函数如果出现默认参数的话,很容易出错. public Person(String name) P ...

  5. 手脱ASPack

    ESP定律到达OEP,重新分析代码块 在菜单栏中找到OllyDump插件,该插件的窗口的弹了出来,有一些选项可供我们修改,我们可以对Base of Code进行修改,这里Base of Code = ...

  6. Python函数学习——递归

    递归函数 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. 函数实现过程 def calc(n): v = int(n//2) print(v) if v > ...

  7. php curl报错:417 - Expectation Failed

    当我在post提交的数据增加一段内容后会报错:417 - Expectation Failed. 查资料发现在使用curl做POST时,当post的数据大于1024字节时,curl并不会直接发起pos ...

  8. 【转】一个 Linux 上分析死锁的简单方法

    简介 死锁 (deallocks): 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这 ...

  9. POJ1456 Supermarket 并查集

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ1456 题意概括  一家超市,要卖出N种物品(每种物品各一个),每种物品都有一个卖出截止日期Di(在该 ...

  10. git release功能

    命令行: git tag -a v3. -m "这是4.0版本" git push origin v3. //git tag -a 标签名称 -m "说明" / ...